本文比较长,也有很多概念比较枯燥,所以我先以故事的形式代入一些理论,引入一些概念和自己utf-8存储方式的理解。然后再讨论一些问题和解决方式,来表达出我的一些不成熟的想法。
从前有一群原始人,需要在自然界生活,他们的天敌很多,迫使他们深居地下,他们白天出去活动觅食,晚上要躲避天敌并且休息,所以会躲在深穴不见天日。这时候就需要哨兵给他们报告白天或阴天,来指示大家是否离开深穴生产交流。现在就需要一种方式交流,于是他们发明了阴阳系统。以数字0代表阴,夜晚;1代表阳,白天。这个系统中有2个符号,我们称为阴阳系统(字符系统,每一个状态代表唯一一种状态)。
阴阳系统促使他们解决了危机问题,但是新的问题出现了。大家出去洞穴后发现大自然很不好相处,有的时候会遇到打雷,有的时候会遇到山林,这时候他们需要交流互相遇到的事物,约定了八种现象:天地水火风雷山泽。于是他么发明了八卦系统来交流,告诉别人他们遇到了什么。沿用之前的阴阳系统表现方式。现在用阴阳系统演变一个新的系统,八卦系统。以八个数字(符号)代表八个卦象。坤(地) = 000,震(雷) =001,坎(水)=010,兑 (泽)=011,艮(山)=100,离(火)=101,巽(风)=110,乾(天)=111。之前的三个阴阳系统叠加为一个八卦符号,即:阳阳阳=乾。这个系统中有8个符号8种状态,我们称为八卦系统(字符系统,每一个状态代表唯一一种状态)。
随着八卦的运用,他们的见识经验也提升了。发展在加速,他们需要更多的生存知识,遇到这八种现象应该怎么办呢?于是他们把每个现象的各种应对方式都列了八种,于是六十卦系统诞生了。六十四卦系统是将之前的八卦叠加,六十四卦的几个例子,六十四卦:火水未济=101010,水火既济=10101,乾=111111,坤=000000。之前两个八卦系统符号叠加为一个64卦符号系统,如两个八卦系统的两个符号水火叠加=64卦系统一个符号既济。这个系统中有64个符号,即64个状态。因此称为六十四卦符号系统(字符系统,每一个状态代表唯一一种状态)。
这些原始人利用自己发明创造的字符系统,运用在各种场所,大大提高了他们的合作交流,也提高了生产力。他们强大之后,已经将天敌驱逐或者放进了他们的菜单中。发展也让他们产生了社会性,他们的组织方式、生产效率、动员能力大大提高之后,逐渐的和发展中的其他部落融合。部落之间交换的东西也日益增多,组织能力也在提升。最后,他们的发展出现了里程碑式的跨越,国家概念形成。并且他们和其他国家联盟,出现了国际联盟。这么庞大的组织出现,使得他们迫切的需要交流工具。于是语言诞生了,语言需要记录,于是文字也出现了。提出语言文字概念并起草文字的组织叫Unicode,于是人们把这个语言文字系统就叫Unicode字符集。文字规范是每个字符占用15个存储单元,每个存储单元记录一个阴阳系统(1或者0),一共存储32 768(十进制)个字符,将各个国家的文字都归纳进去,统称万国码。
由于大家都需要将文字记录下来,所以需要有载体。一个叫“二进制”的人发明了一种竹签刻字的方法,以此作为载体可以存储。文字存储到“二进制”的竹签中一个“我”字存储的书写格式为:000,0000,0001,0000,每个字符都需要占用15(十进制)个存储单元。这样才能区分出完整唯一的字符,从而读出完整的信息。这种方式存了很多高位上的0,很是浪费存储空间。
起初读写文字,大家都是按照规定存取的。慢慢的,“二进制”发现存储的材料越来越少了。于是让大家想办法节约空间。有人发言,大家都将各国的文字系统集中起来编码,自然费空间。不如各国用各国的文字系统,这样就省事了呀。有人反驳这样就没有统一的编码了,但是还是有人采纳这个意见创立了他们国家各自的编码来节约空间。又有一个人提出用其他物理载体存储吧,“二进制”直接反驳,我的方式是最稳定,最安全,技术最成熟的方式。你的方式稳定吗?安全吗?技术成熟吗?这个建议也被否决了。
直到有一天出现了一个叫“utf8”的人,提出了优化“二进制”的Unicode编码存储方式。后来大家就把这种编码存储的方式叫utf8编码方式。Utf8的编码中心思想是将等长的存储方式换为变长的存储方式。
Utf8首先说,你们把存储的载体,就是那个竹签,规定每个竹签固定尺寸只能刻录8个阴阳系统,即8个存储单元。比如:11111111,这是固定尺寸的最大存储量也是最大值,每个固定尺寸为一节,即字节。 然后就是存储字的值了,可以看到,我们的“我”这个字的Unicode(故事中的Unicode)存储书写方式是“0000000,00010000”,占了15个存储单元,即15位。我们知道高存储位的0都是没用的,因此需要浪费很多存储单位去存那些0,也没有实际意义,只是为了凑齐15个存储单位去辨别字符的唯一,这叫等长存储。现在可以去变长存储,我们以字节(1字节 = 8个存储单位)为基本存储单元去存取数据,原因后面来讲。
我们先从一个理论说起,假设存有2个字符并且每个字符都具有唯一性的字符系统叫2进制,那么阴阳系统就是2进制,八卦系统就是8进制,64卦系统就是64进制。我们的Unicode字符集也能保证字符的唯一性,一共可以存储32 768个字符,那就是32 768进制。由于存储的载体只能画0或者1,因此无论什么字符都将转化为2进制,以2进制存储,这是技术限制(听说邻村有个人发明另一种读写载体,叫量子,如果量子有8种书写形态,那么任何的数值就又以8进制为基础存储数据了)。当我们用32 768进制的某值去转换为二进制进行存储的时候,高位有大量的无用的0,我们称为泡沫。
Unicode一共有32 768个字符,但是我国只用到了2000个字符,而且常用的只有500个字符,字符集利用率非常低。如果将我国的常用字符进行编码和读写,将会非常省空间。所以可以看出,上述的值过大并且泡沫过大的直接原因是因为那些利用率极低甚至不用的编码占取编码值所致。如果一个字符集的每一个编码都是常用编码,也同样会有大量的泡沫。但是无用或者效率低的编码会将泡沫放大很多倍,使得我们所存储的有效值都不一定有意义。如果只用一个国家常用的文字系统来编码,那编码是从0开始的,并且很多字的值还是比较小,泡沫自然不多。编码序列靠前的国家就有这个好处。序列靠后的国家编码起步很高,如我国,编码从万开始的,这样泡沫也充斥在每一个有效值中。
其实4进制已经足够存3了,比起32 768进制,4进制节约了大量的空间,减少了大量的泡沫。这样一来,我们将32 768进制的每一个字符开始编码,从小到大的值依次从0到32 767,我们用4进制存储小于4的值,用8进制存储小于8的值,用64进制存储小于64的值,这样一来就可以消除掉很多很多的泡沫。同样的存储空间就会比以前存储更多的字(数据)。
然而,数值存储的问题解决了,但是那么多进制我们怎么知道哪个值是什么进制存的呢?如果存100个数值用了10个进制,我们存了倒是挺方便,但是读的时候怎么知道哪些值是哪个进制存的呢?这样就乱套了。因此我们在存储数值的时候,也要存储值的进制,并且数值还要和进制进行区分开来,不然混在一起也没办法识别。我们需要在值和进制之间权衡,选取几个比较合理的进制进行存取值,这样存储值得同时存储对应的进制。
之前说过,存储的空间分为固定长度,每个固定长度存储8个存储单位,即一个字节。我们可以将数值和存储进制存储在以字节为一个单位的竹签上。这样就可以解决小数值的泡沫问题了,但是同样会存在大量的泡沫,这也在进制和值之间权衡的办法了。我们规定,7个存储单位的值,用一个字节存储就行,前面空出一个单元标记存储的进制,“0xxxxxxx”。11个存储单位的值,用两个字节存储,两个字节的前面分别标记存储的进制和值是否存储完毕的状态,“110xxxxx 10xxxxxx”(前5+后6=11)。以此类推三个字节存储值的是16个存储单位的值,“1110xxxx 10xxxxxx 10xxxxxx”。这样我们就可以选取几个固定的进制,去存储不同大小的值。这是泡沫和进制权衡的一个办法。
我们可以看到,一个字节可以存储的值,是用一个字节存储的。两个字节存储的第一个值不是从0开始的,而是从一个字节最大数+1开始的,比如“11000000 10000001”是两个字节存储,这个值不是1,而是(2的7次方 -1)+ 1 = 128,这样可以保证不存储重复的Unicode值。在此,我们还要注意,前缀为“0”的字节,一定是2的7次方=128进制,它可以存储128个值。前缀以“110”开头的字节肯定是2的11次方=2048进制,它可以存储2048个值。前缀以“10”开头的字节肯定是没有读写完成的值。
Utf8的这一套变长存储的办法,将万国码Unicode字符集以一种科学的方式进行管理和读写,从一定意义上也节约的存储空间,使得同样大的空间内可以存储更多的字符。于是,utf8成为了一种编码方式,并且被广泛应用。
过了很久之后的一天,一个进行计数的工作人员tinna开始学习utf-8的编码方式。令他不解的是为什么要这样做呢?于是他问linnda计数员几个问题:
1、tinna问为什么用“0”“110”、“1110”来区分进制呢?我们用0/10/11…这样不好吗?
2、linnda回答:这样就无法区分那个是进制哪个是值了,你可以看到,每一个字节从高位往后读,第一个0的的下一位就是值了,这样就可以区分进制的编码和值的编码了。
3、tinna又问为什么要给每一个字节前面都加进制编码呢?3个字节的值,只需要在第一个字节标明值就行了,剩余两个字节就不用标记编码了,这样可以存大量的数据呢,不是更好吗?
4、linnda回答:那么,如果一段字节丢失了或者变得模糊了怎么办?你读取模糊那段后的第一个字节,如何确定它到底是值呢还是进制标记呢?如果确定不了,那么任意一个字节的丢失,会造成所有的数据都成了一锅浆糊。当我们给次字节(双字节以上的编码首个字节后面的字节叫次字节)引入“10”时,我们会发现,只有次字节的前两个位是“10”,进制标记从“0”到“110”故意绕开了“10”,这样可以保证主次有序,当你读取每一个字节的前几位是就可以确定它是多少进制存值,它是主字节还是次字节。这样如果有一段字节丢失,我们可以根据这种方式区分一个个的编码,还可以将编码翻译为字符之后根据上下文猜测丢失的字符是什么,从而猜测出编码,然后补全。
5、tinna仔细想想觉得这个解释很合理,然后他又问,为什么单字节的编码用“0”来记录进制,而双字节又用“110”,三字节的编码用“1110”呢?次字节用“10”呢?如果将“0”和“10”颠倒一下顺序,“10”代表单字节,第一位为“0”表示次字节,那每一个次字节都将多出一个存储单位来存储数据,而单字节的编码会损失一个存储单位,单字节存储量会折半,但是对于多字节的编码来说会是很大的好处。比如双字节的存储量是之前的2倍,三字节的存储量是之前的4倍。四字节的存储量是以前的8倍。为什么不这么做呢?
6、linnda回答:你说的很对,但是单字节的128个字符存储的是一个编码系统,有一个国家叫阿迈瑞肯,他们国家信息部的标准字符系统正好是128个编码。那套编码叫阿思卡码。他们国家的国家信息标准字符刚好可以用utf8的第一个字节存储。可能当时想法就是尽量用一个字节存储阿思卡码,所以用“0”来标记第一个字节的进制。这个是猜测,具体原因不知道。至于双字节三字节和四字节都可以大大的扩展出存储空间,即便扩展出来又有什么用处呢?单字节用“0”标记倒是对于阿思卡很有用处的呀。
tinna和linnda的对话解释了utf8设计时的思路,当tinna看到第一个字节用来存储使用率非常高的阿思卡字符系统,也感到这样很合理,因为阿思卡编码在世界各国中基本都会使用,通用而且存储单位小,存储效率很高。
过了一段时间之后,有一个人发现了存储的一个特点,条理如下:
1、用八卦到六十四卦的使用率现象,表明离散区域的命中率问题(使用频率极低甚至接近0的状态叫命中率,使用率一般的状态叫使用频率。):如果八卦中一个卦象不经常用,那它在六十四卦中的八个卦象也不经常用,而这八个卦象如果又排列在双字节中,将使用频率高的卦象被推到后边的三字节中,这样造成双字节编码的八个值槽经常空置,降低了双字节编码区间值的命中率。而三字节经常命中的编码又很多,这样会造成双字节的空置和三字节的繁忙,降低了存储效率。如果经常命中的三字节编码和不被经常命中的双字节编码互换,会提高小范围内的命中率,经常用三个字节存储的字符也只需要用双字节存储,也就提高了存储空间,降低了值泡沫。
2、如果一个双字节编码的字符1天用10次,它存储了20字节。如果一个三字节编码字符一天用100次,它存储了300个字节。如果将两者编码调换一下,此前双字节字符存储为30字节,而此前三字节字符存储为200字节,节约190字节。按照使用频率排列是最节约空间的一种方式,比如每个国家常用字符按照顺序排列,不常用的字符等到各国常用字符排列完之后再去排列,就会节省出大量空间。
3、假设世界有七大语言系统,分别用红、橙、黄、绿、青、蓝、紫代替,使用率较高的常用字符用“热”来标记,使用率很低的字符作为扩展字符集,用“扩”来标记。最节约存储成本的字符排序应该是红热、橙热、黄热、绿热、青热、蓝热、紫热、红扩、橙扩、黄扩、绿扩、青扩、蓝扩、紫扩。按照这种区间去排序,提高了靠近0的范围内使用值槽的命中率,也降低了靠近无穷大范围的命中率。即使那些一年也用不了几回的字符,我们调换位置后用1000个字节存储,调换编码符号所带来的空间存储也会大大的节约。字符命中率和使用频率会影响存储效率。
因为这看起来像是将不同国家的字符一遍又一遍的编排区域很像彩虹,所以叫这种排序叫“彩虹排序”。这个主意确实不错,仅仅调换一下位置,就可以节约大量空间。如果是你掌管unicode和utf8,你可以随意更改,倒不太难。但如果你没有话语权呢?那这就不是一个简单的计算问题或数学问题,而是话语权的问题,是一个国际性问题。即便大家都同意,新旧编码如何兼容也是问题。
最后,这个观点虽然被提出来了,但是没有人去采纳,最有话语权的国家,他们的所有字符编码都在第一字节,这中排序并不能为他们带来利益,而且兼容问题还挺棘手,所以他们高高挂起,不闻不问。
还有一个补充,比如将utf8中的“0”和“10”标记进制的编码调换位置,将会有很多的字符从第三字节上岸爬到第二字节,同样会有更多的字符从第四字节上岸爬到第三字节。加上常用字符和不常用字符都调换位置后,将会节约大量的存储空间。
但是这不符合阿迈瑞肯国家的利益呀,为什么要牺牲阿迈瑞肯国的利益而成全你们呢?可能之前指定规则的那个年代,阿思卡码就是标准,使用频率很高,人家是引领者,是权威。凭什么让步呢?要站在阿迈瑞肯国的立场想问题,给他们也带来利益,人家才会做出让步。
在很长时间里,虽然彩虹排序被提出,但是由于具体操作遇到的种种非数学领域的问题,所以没有实现。直到又有一个人提出了新的观点,给阿迈瑞肯国带来了利益,大家觉得这个观点可行性比较好,会给大家都带来利益,所以很看好这个观点。条理如下:
1、协议的本质即约定,没有逻辑,也无其他含义。如3x3=9,这跟逻辑没有一点关系,它只是十进制九九乘法表协议的一个条款,这样3x3=9才有应用意义。 比如一个智商正常的成年男子,计算12x12,结果为144。他是按照十进制(十进制实际上也是数学上的一个协议,一种数学规则)规则一个个加出来的,也可以得到正确的值。当他看到3x3=9时都不知道这是什么表达式,自然也不会用九九乘法表这个工具。但是当他知道这个协议,并且知道这个协议的基础协议是十进制时(这点很重要,八进制为基础的七七乘法表,3x3=11),也就知道怎样使用了。
2、一个64进制系统占用6个存储单位,实际上就是6个长度内,2进制任意6个字(符)的一段话。是4进制的任意3个字(符)的一段话,8进制任意2个字(符)的一段话。那么我们有理由相信,Unicode任意一段话,无论是多少个字,都将是某进制的一个值,那个值对应一个字符。你不觉得“1357”这个十进制的值,实际上就是十进制字符系统的一段话吗?它同样也是10000进制的一个编码,一个10进制的4个字符,在10000进制中可以用一个字符取代。
3、讨论这一点时,我们需要知道前三条。在表达上“大家”这个词由“大”和“家”组成,但是在词的概念里,“大家”是一个新的基本单位。如果我们有一个字符系统是100个编码,我们可以增加一个“大家”这个词作为一个字符,给他新的编码,那它就是101。以往“大”的编码为89,“家”“90”,如果用变长的3个字节去存一个编码,那他们的字节总长为6个字节。但是如果“大”和“家”有一个协议,即:“大”“家” = “大家”,换做编码表示就是:“89”+ “90”=“101”那么它存储值就是101。或者是3个字节或者是4个字节去存储之前6个字节的字符,这样就节约空间了。我们将“大”“家”称为原始字符,将“大家”称为协议字符。找出10个使用率最高的词,作为扩展字符给它们编码,并区分出协议字符的区间。再将这些词语与对应的字符数组建立一个协议对照表,即可存储对应的协议编码,取的时候按照对照表复原成对应的单个编码的字符。
以上,读写时都是按照字符协议去执行,可以节约写的存储空间并且不影响读取后的质量,但是需要有一个人去监听并且执行这些步骤。因此,这种方法是以时间换取空间的一种策略。如果当前找的不属于对应的协议字符或者检测的人眼花没有检测到,那还是按照之前的规则存储,不影响正常存储规则,容错率很高。
等长最多将字符扩展到当前编码系统二进制长度的最大值,超出这个范围每一个字符编码都将多出一位。如36000编码的字符系统可以加长至65535,位长度不变,这样在扩展的空间中加入词,将会省略很多空间。
变长可以扩展很多词,但是变长存储考虑的问题就和等长的不同了,变长要考虑词的位置放在那。如果一个变长存储最大的编码有6个字节,而常用的两个字符都是一个字节,这样组合的新词,如果往6个字节后面续编,反而增加了存储体积。但是如果往编码的中间添加。比如三个或以上的字符组合的词加在第二字节这个区间,将会节约一个字节。使用频率越高,越节约空间。
根据条理2可知,如果某个字符不常用,那么它的组合的词语或者句子就不常用,就会存在大量的数值区内值槽空置,而把使用频率高的字符挤到靠后的值槽,这样就是浪费空间的原因。若将频率高的字符靠前编码,就是将此编码的词组、句子的组合的值靠前排列,这样所使用的字节自然就小。字符组合是固定的,但是他们字符元素编码序列不同,值就不同。
信息记录中万事万物皆为数,有的数小如水滴(如单字符,是字符集合的基本单位),有的数大如汪洋(字符集,如文章、书籍)。当数(数值)不变,理(进制)变化时,它的象(长度)也随之变化。象(长度)只是现象,理(进制)才是变化的原因。但是无论怎么变,数(数值)不变,这是象数理之间的内在联系。
===== 故事完结 =====
我们处在信息爆炸的时代,处在大数据时代,如果可以优化存储方式,将会节约很多的成本。
很难想象我们所处的大数据时代,每天会有多少磁盘在不断的读写数据。Unicode标准和utf-8制定时,有没有意识到未来的信息爆炸和大数据呢?也许现在的“0”和“10”的标记方式是他们无意而为之,因为后面粗放型排序就可以看出。Unicode根据国家字符集的先后去编码的,有的国家冷门的扩展字符排的很靠前,命中率却很低。而使用更加频繁的字符却在靠后的区间,增加了存储的成本。未来将会有比现在更加巨量的数据产生,每天存储成“吨”的数据,而如果存储方式本身会产生泡沫,我们将会浪费资源。损失最大的,可能是世界上的云存储服务公司,比如亚马逊、微软、阿里巴巴。
上述故事的论述,是我想出的三种优化方式,分别是utf-8字节标记“0”“10”调换、“彩虹排序”、“协议字符”。有的时候优化最终是否可以执行取决于现实情况。比如要让大家都看到优化之后的好处,其中让最有话语权的国家有利益可图很重要,几乎关系到优化的可行性。有时候的优化,并不是所谓的逻辑问题或者数学问题,而是话语权问题和利益问题。
我们知道最有话语权的国家当然是美国,美国的ASCII码也是毫不客气的霸气的存在于utf8的第一字节,无论国际存储问题风吹雨打,都与美国无关。因为这就是第一字节的优势。utf8编码中进制标记“0”和“10”是今天这个样子,如果真的只是为了第一字节可以完全容纳ASCII码而专门设计的。那我们看到,第二字节的存储量损失了一倍,第三字节存储量损失了四倍也会是事实,这将造成其他国家的字符存储的巨大负担。
我看到很多国家的字符系统,是按照国家字符系统来整体排序的。扩展字符并没有和常用字符分开在差值较大的两个区间。这样的排序明显会增加靠后常用字符存储负担。在当时进行Unicode排序的时候,大家有没有想到我们会进入一个大数据时代呢?并且信息的生产速度还在快速增长。
如果真的要优化,其实在三个方式中,Unicode重排成“彩虹排序”的可能性最小,这可能牵扯到新旧Unicode编码兼容性的问题。而“0”“10”调换和“协议字符”的组合可能性比较大。我们只要另起炉灶,重新组织一种编码方式utf-8II。将“0”“10”调换,扩展出来的值槽我们专门存储“协议字符”。这样的情况下,第一字节损失64个值槽,第二字节增加2048个值槽,第三字节增加196608个值槽。
这种编码方式,像阿里巴巴、亚马逊、微软、谷歌这些公司内部自己都可以搞定,公司内部使用这种方式编码,可以在第二字节扩展的2048个值槽都存储“协议字符”,如果这2048个协议字符的是使用率为12%的中文字符,我们暂时称之为utf8-chinese,可以想象之前要存储6个字节现在只需要2个字节,12%的成本降低为4%,将节约8%的成本,这还没有计算第三字节196608个值槽的应用。也许我们在扩展的值槽中存储数千个中文词语,让降低10%的存储成本成为可能。我们也可以灵活的取舍,如在计算速度和节约成本之间权衡,只需要协议字符即可。假设消耗的时间可以接受,使用场景可以是数据库备份、压缩数据和任何时间换取空间的场景。其实只要不是实时性要求很高的业务基本上都可以应用到utf8-chinese。
这种编码方式大公司内部使用可以降低存储空间,如果有个更大范围内的标准更好,比如有个中国国家信息标准utf8-chinese,规定双字节前缀为“1101”的编码区间是协议字符的区间,并且编制编码与协议字符对照表。前缀为“1100”编码区间为传统的utf-8编码区间不变,按照Unicode序排列。这样我们按照这种标准去读写协议字符,国内或者国际上数据之间传输或者拷贝可以直接拷贝字节流,只要知道这个编码方式是utf8-chinese,按照对应的标准去读写即可。
如果有个国际标准也可以,编写一个编码与字符协议对照表,为国际通用标准。其实各国之间自己的utf8“协议字符”对照表已经足够了,这种编码可以直接转为Unicode,然后进行各种编码之间的转换。Unicode的目的是世界各种字符的统一标准,ASCII和GBK做不到兼容各国的字符,但是上述的utf8-chinese可以做到。即便扩展了编码,但是它仍然只是Unicode的一种编码方式。没有世界统一标准,各国用各自的反而可以充分的利用第二字节扩展区间。如果要通用也很简单,跨标准传输时只需要统一转为Unicode即可,或者传输时只需要知道是哪国的编码对照表标准,然后自行转换即可。
大数据时代很多云服务公司为世界提供云服务,编码优化可以提高他们的竞争力。“协议字符”对于各个语种都可以应用,我们用中文和英文来对比一下,看看英文在“协议字符”方面的优势在哪。
从中英文书写上我们可以看出,英文一个字母长度很短,单个汉字占的长度很长,但是一篇文章下来,往往汉字长度比英文短。为什么中国书写的文件总体占用小呢?
因为汉字字符系统进制比英文字符系统进制高,拉丁文52个字母(含大小写)相当于中国文字符号的偏旁部首。英文的每个单词是一个个排列出来的,而中国的字是将不同笔画捏在一起重新生成新的符号。从某种角度看,这本身就是一种“协议字符”。我们知道中文笔画为1的和笔画为20的文字符号占用的空间长度是相同的,这种方式最终让单个中文书写占用的空间,与笔画数没有半点关系。而英文词汇的占用空间恰好相反,笔画的多少决定占用空间的多少。中国的字、词是等长书写方式,美国是变长书写方式。
如果老外学习中文字符将会先学习什么呢?当他们学习文字结构时会不会惊讶呢?“左右结构”、“上下结构”、“左中右结构”、“上中下结构”、“半包围结构”、“全包围结构”、“镶嵌结构”等等,这些特点在英文中没有,英文的单词只有左右结构。虽然书写占用空间很多,但是他们记得词汇量可并不比中文少,这是中文“协议字符”的特点。
中国以两万个字符计算,两个字符叠加可以组成4亿(20000的平方)种形态,而美国只能组成2 704(52的平方)种形态。如果每个词代表一种意思,那中国可以囊括4亿种意思。从概率学上来说,大多数情况下,中文书写比英文更节省空间。在一段话中,中文是紧凑型的,可以紧挨在一起,而英文每个单词还要用空格间隔。因此书名存储效率没有中文高。
而恰恰是因为英文单词有空格间隔,更利于检索。每一个空格相当于一个词汇的结束符,很容易捕捉词汇长度。然后再通过“协议字符”降低词汇的值,低值可以存放在比较靠前的值槽,从而提高存储效率。计算机时代的数据存储,英语可以依靠“协议字符”降低存储长度,避免占用空间决定于笔画的多少这种情况。虽然在文件书写上英文偏长,但是在计算机存储中,英文可以做到和中文同样的效果。中英文在书写上长度有区别,但是内在的数理相似。
如果utf-8编码将“0”“10”倒置后,会将Unicode字符编码在64~127区间的字符转移到第二字节。而拉丁文编码从65开始的,就在这个区间之内,这样导致的直接后果是所有拉丁文的存储将从1个字节变为2个字节。拉丁字母的应用非常广泛,从世界交流语言来看,英语是世界上非常流行的语言。从计算机编码角度看,英文的应用也非常广泛。因此,将拉丁字母从1个字节转为2个字节存储将会增加大量的存储空间。
这样就需要一个协议,规定假设当前Unicode编码为code,如果code在0<code<128这个区间,将编码65转为编码1,66转为2…,这样避开编码32(空格),空格也是非常常用的字符。同样避开48~57,这10个编码是阿拉伯字母。协议对照表的目的是将52个拉丁字母转进编码64以内。最终将“null”字符、“空格”字符、10个阿拉伯字符、52个拉丁文字母字符,总计1+1+10+52=64个字符。让这最常用的64个字节保留在第一字节,剩下的64个ASCII码保留在第二字节。
按照上述的字符协议进行读写操作,只需要列出协议对照表,当且仅当0<code<128进行协议对照。这样对于计算的损失也不大,可以尽量对常用字符存储止损。
第二字节的区间中将会多出2048个“值槽夹层”,可以新添加2048个协议字符,此空间的2048个槽值也是在连续的区间之内,如果当前编码在这个区间,可以拿出另一张对照表来存储对应的“协议字符”,这样就相当于将很多字符“打包”存储。读写时按照对应的规则,可以将对应的字符数组转换为“协议字符”,将协议字符编码存入槽值,这样可以大大的节约空间。
第三字节多出的“值槽夹层”与第二字节的区间同理,此时可以存储的协议字符将有196608个值槽,可以做很多事情,协议也更加的庞杂,但是同样是一个连续的区间。只要是连续的区间,很容易按照不同的协议对照,简化操作。第四字节同理。
第一字节共有104组协议,为“0”“10”止损,置换ASCII码;第二字节有2048组协议,用来扩展协议字符,数量并不算太大,对计算速度影响较小,是扩展的黄金区间;第三字节196608组协议,是广阔天地大有作为的地方。中文编码在第三字节,如果将常用词语或其他组合字符存储在第三字节“值槽夹层”,将节约很多空间。
在我们这个大数据时代,或者以后数据的生产量可能呈指数级增长的时代,存储数据是一个非常重要的事情,它不仅仅是节约成本的问题,还是我们将如何面对未来并且管理未来的问题。如果“0”“10”置换和“协议字符”策略是可行的,那么这种编码方式最终还需要一个统一的国际标准,因为我们知道在互联网时代编码不统一也会消耗成本,有成本代价。比如数据生产和软件开发,程序员所用的开发工具和持久化数据的技术总会选编码方式。那些软件公司不会给每个国家都去开发一个编码方式,最终还是要统一传输标准和生产标准。除非各个国家有能力自己去扩展自己的标准。这个时候就需要像美国这样的权威站出来,去制定一个标准。
如果统一标准,第二字节的2048个扩展值槽会存储些什么呢?可能是更庞大的机器语言指令、最流行的十大开发语言的最常用的20个关键字、英文单词…具体的不得而知,但是只要能迈出utf8“0”“10”调换这一步,计算机存储的效率将会提高。
本文所表达的只存留在理论层面,具体是否可行,需要实际的评估与测试。我说了不算,也这不仅仅是数学问题,而是一个更加复杂的问题。我不知道这些论述是否行得通。但是可以确定的是,大数据时代已经来临,5G时代也将马上到来,数据生产的速度将会不断的增加,低效率的存储方式将会增加大量的数据泡沫,存储大量的垃圾。当我们将同一组所表达的字符存储一遍,真实存储时,存储它的“协议字符”(也可叫引用),可以减少大量的重复存储带来的开销。以此种方式去减少数据冗余。