Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
36 lines (28 sloc) 5.37 KB
title tags categories date
字符集与字符编码小史
GBK
Unicode
科普
底层
2015-11-16 04:33:19 -0800

现在仍常使用的最早的字符集就是所谓的ASCII字符集,包含128个码位(也就是说,共有128个数字可以对应到不同的字符),含有一些控制字符,如换行符、字符串结束符、制表符等等,英文字母大小写、数字、空格、一些英文标点符号。我们知道计算机中一个字节是8位二进制,如果一个字节恰好存储一个字符的话,应当有256个码位。ASCII字符集只使用了其中的前一半,所有ASCII字符在用字节表示时,最高位都是0,较低的7位为ASCII值。

后来,各个国家制定了本国语言的字符集。为了和ASCII兼容(毕竟ASCII已被广泛使用,如果新的编码方式不按ASCII来编码英文字母和数字,会造成不便。另外,新编码方式如果有可能被误读为ASCII的话也不好),普遍采用了以下方法:若是ASCII中本来就存在的字符,仍按ASCII存储。新增的本语言字符在存储时,字节的最高位为1。这样相当于将ASCII剩下的128个码位利用了,不和ASCII冲突。不过,比如中日韩表意文字,数量远远超过128个,ASCII剩下的码位不够,怎么办呢?

以GBK为首的大批中文编码采取了这样的方式:ASCII中存在的字符原样表示,而每个中文字符都用连着的两个字节表示。这两个字节的最高位都是1,以免和ASCII混淆。这样一来,中文拥有了2^(2×7)=16384个码位,基本上够表示常用字符了。这样一来,各个语言都有了自己的字符集,它们都不与ASCII冲突了。但这种编码仍然有很大的问题,一是各个字符集之间互相冲突,收到阿拉伯友人发来的邮件,用中文编码打开是乱码;二是没有任何一个字符集含有全部语言,无法撰写一篇中文阿拉伯语并存的文档;三是GBK这种编码如果在传输时丢失一个字节,或者文档恰好从一个字的两个字节之间被截开了,就会导致接下来全部乱套,前一个字的第二个字节和后一个字的第一个字节会被机器理解为同一个字对应的前后两个字节。著名的“俸俸伲购美病”就是这样产生的;四是进行搜索和替换不方便,设x,y,z三个汉字的编码分别是AA,BB,AB(每个大写字母代表一个字节),那么机器容易错误地以为“xy”这个串(AABB)包含z字符(AB)。

在这些混乱霸占了计算机世界很多年后,名为Unicode的救世主出现了。Unicode是这么做的:首先定义了17个“平面”,每个平面包含2^16=65536个码位。这一步仅仅是为所有字符分配编码,完全不涉及写成字节来表示的问题。17×65536=1114112个码位可以说是足够存储全世界任何语言的任何字符了,还能分出不少存储一些特殊控制字符或者小图形(emoji),甚至极为阔绰地分了整整两个平面作为“专用区”,不定义字符,用来存储使用者自定义的符号。同样,出于和ASCII兼容的考虑,它规定第一个平面的最开始的128个码位和ASCII完全相同。再次提醒:Unicode本身不涉及“字符怎么存储进字节里”这个问题,它仅仅解决“给所有字符进行编号”的问题。

下一步,Unicode规定了几种(没错,不止一种)字符编码,比如UTF-8(最常用)、UTF-16等等。以UTF-8为例,规定存储为字节时格式如下(下划线上填写该字符的编号的二进制): Unicode的前128个码位:0_______ 2^11以内的码位:110_____ 10______ 2^16以内的码位:1110____ 10______ 10______ 剩下的码位(也就是除了第一个平面以外的):11110___ 10______ 10______ 10______

这种存储方式,针对性地解决了以前的字符编码存在的各种问题。它包含所有语言,可以作为世界通用的标准编码来推广,避免了编码混乱和冲突;任何一个单独的字节,不需要上下文就能判断出它的类型(0开头是ASCII,10开头是某字符的不完整的后续,110开头的是一个2字节长度的字符,1110开头的是一个3字节长度的字符,11110开头的是一个4字节长度的字符);被截取的文件片段和传输时被损坏的文件也能尽可能解码了;搜索和替换程序只要简单地进行比较就行了,不用处理诡异的“AABB”包含“AB”问题了。

总结:

  • ASCII是一种容量很小的字符集,共128码位,但使用广泛,所以一切其他编码都要和它兼容。
  • 字符量少的语言直接将自己独有的字符安排在ASCII剩下的128个码位上。
  • 字符量大的语言将两个“剩下的128个码位”合并,利用16384个码位存储自己独有的字符。
  • 这些粗糙的方法都有很多缺陷。
  • Unicode字符集为全世界所有字符分配了编号,但不管这些编号数字该怎么表示。
  • UTF-8、UTF-16等编码方式规定了Unicode的编号该怎么妥善地存储在字节中,这种存储方式非常先进,没有冲突,容错性好。
  • 我们应当使用UTF-8编码!稍有常识的人都能看出,如果Unicode的铁骑继续前进,Windows cmd这种使用GBK的螳臂当车的歹徒,难道能够阻挡得了吗?