简单谈一谈字符编码这点事儿
一直就想写一点关于编码的东西,这一点点概念虽然很小,甚者你可以认为这很简单,但实际工作中发现,真正能把几种编码形式,从概念到应用理解清楚的,或者讨论问题中,能把编码这件事解释清楚的,真不多。于是花了周末的一个下午,把这点事儿简单写了写,结合自己工作中的认识,自认为算是说清楚了,贴出来与诸君共勉。
所谓编码,其实就是一种信息的组织传递方式,原始人,没有文字时,跳舞,打手势,严格来讲都是编码,乃至后来的战国时期兴起的烽火,抗战时期的鸡毛信,严格来讲都算一种编码方式,今天重点介绍,日常用的比较多的几个编码概念,ascii,gb2312,gbk,utf8 ,unicode等几种。
先说最简单的,ascii码,如果全世界就英文这一种语言,ascii就可以搞定一切了,简单来总结,ascii码适用所有拉丁文字母,使用7 位二进制数来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。而最高位为1的另128个字符(80H—FFH)被称为“扩展ASCII”,一般用来存放英文的制表符、部分音标字符等等的一些其它符号。
最高位为1的128个扩展码并不常用,重点介绍一下,最高位为0的128个字符,其中:0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),32~126(共95个)是字符(32是空格),其中48~57为0到9十个阿拉伯数字,65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。
遗憾的是,ascii码只能处理英文了,一遇到我们东方方块字就没辙了,于是,国家某部委,终于干了一件至今我认为比较靠谱,且有技术含量的事儿:制定了gb2312编码规范,全称是中国人民共和国汉字信息交换用码。除了表示了我们常用的简体汉字,还处理了希腊文,日文,及俄文等西方字符。必须承认,这套gb2312标准的实现,比ascii码标准的实现确实要难上几个系数,成千上万的汉字,并不是一个字节,128个符号就能表示的,于是gb2312采用了区位码的概念。何为区位码?可以理解为一块地图,诸侯割据,划分成94个区,然后每个区,又划分成94个位,共划分了94*94个小格子,每个格子表示一个字符,这个字符的编码就是这个格子的区码和位码的组合,这个编码形式就叫区位码。区位码一般用10进制数来表示,如4907就表示区位码中49区第7位,对应的字符是“学”。区位码中01-09区是符号、数字区,16-87区是汉字区,10-15和88-94是未定义的空白区。它将收录的汉字分成两级:第一级是常用汉字计3755个,置于16-55区,按汉语拼音字母/笔形顺序排列;第二级汉字是次常用汉字计3008个,置于56-87区。
但是,这么一来,存在一个问题,区位码编码范围和ascii码存在重叠,重叠的部分,机器无法区分这玩意属于ascii编码,还是属于区位码。于是,为了便于传输和表示,每个字符的区位码,又分别加上了两个数字20H(避免同ascii控制码冲突),和80H(避免同高位为1的扩展ascii冲突),即20H+80H = a0H, 区位码的区号和位号上分别加上A0H就得到了GB2312编码。
所谓gbk呢,也是区位码,只不过,区位图上不再是94*94的划分,又多划分了一些区域,包含了更多的汉字和字符,但编码规范上,完全兼容gb2312,即一个汉字的gb2312编码,和gbk编码完全一样,比gbk更大的汉字编码集是gb18030,同样也是区位码,只不过范围更大一些而已。
终于要说unicode了,天下这么大,如果每种语言,每个字符都用区位码来表示,区位码的范围将会更大,当然这也未尝不可,但问题是,咱们部委的这套编码标准,其他国家的标准委员会未必认同,于是,unincode编码规范,应运而生了。这种编码为每个字符,包括世界上所有语言文字,包括以后可能出现的火星文和不知道啥星文,都分配个一个数字编码,这个编码叫unicode编码,编码范围从0-0x10ffff,最多容纳1114112个字符,按说这就太平了,可是考虑到字的边界问题,必须对这些数字设计一个编码规范,否则一屏幕的数字,你怎么区别,到哪儿算一个完整的字呢?字与字之间如何划分边界呢?于是针对不同的unicode编码规范,出现了utf-8,utf-16,utf-32等编码规则。UTF-8、UTF-16、UTF-32分别以BYTE、WORD、DWORD作为编码单位。“汉字”的UTF-8编码需要6个字节。“汉字”的UTF-16编码需要两个WORD,大小是4个字节。“汉字”的UTF-32编码需要两个DWORD,大小是8个字节。这里重点介绍一下utf-8,这是日常用的最多的。
UTF-8以字节为单位对Unicode进行编码。从Unicode到UTF-8的编码规则如下:
Unicode编码(16进制) UTF-8 字节流(二进制)
000000 – 00007F 0xxxxxxx
000080 – 0007FF 110xxxxx 10xxxxxx
000800 – 00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000 – 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
UTF-8的特点是对不同范围的字符使用不同长度的编码。对于0×00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。从上面的utf8编码规则可以看出,4字节模板有21个x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。UTF-8的第一个字节开始的1的个数代表了总的编码字节数,后续字节都是以10开始。
例1:“汉”字的Unicode编码是0x6C49。0x6C49在0×0800-0xFFFF之间,使用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
例2:Unicode编码0x20C30在0×010000-0x10FFFF之间,使用用4字节模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将0x20C30写成21位二进制数字(不足21位就在前面补0):0 0010 0000 1100 0011 0000,用这个比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。
utf-16,utf-32不再赘述,与utf-8的编码原理相同,不同的只是编码单位分别是2个字节,和4个字节。
顺便纠正一个日常工作中错误的说法,码农在涉及编码的工作中,常说就是gbk编码到uf8编码的转换,其实这种说法不确切,gbk确实是一种字符集标准,但utf8并不是一种字符集合,严格点说应该是gbk字符集到unicode字符集的转换,转换成的unicode编码,用utf8规则来表示。好像有点绕嘴了,但确实要这么去理解。本人曾遇到过更有甚者,讲gbk也是unicode字符集的一种编码格式,当时无语,你可以有病,但我可没药,这种基本概念要是要搞搞清楚的,工程上的概念,该严谨还是要严谨。
目前主流的编码集,就上述几种,结合自己工作中的认识,简单总结了一下,说到这里顺便提一下,乱码是怎么产生的呢?文件a用utf-8的规则,来存储了一篇文字,电脑的cpu指令却准备用gbk 的区位码规则,来解析这些文字,并显示在屏幕上,于是乱码产生了,所以说,其实本没有乱码,只不过,鸡同鸭讲时,彼此都听不明白而已。
轻松一刻
- 所有评论
上一篇: 这一年,过的快–我的2014年终总结
下一篇: 周鸿祎冰水浇头,接受als慈善冰桶挑战