首先,标题中的Byte不同于另外4个词。Byte表示字节,是一种计量单位,8个比特为1个字节。可以表示的最大数字是11111111,十进制是255。有了Byte,如何将他变成人能看懂的文字呢?
于是就有了ASCII、GB2312、Unicode、UTF-8,它们分别表示一套编码规则,相互独立,但之间又有一些关系,剪不断理还乱。但作者相信,经过本文通俗易懂的翻译之后,您一定发出一个感慨:“就这!”
先简单介绍基本概念,如果没时间可以跳过,直接看最后的关系总结。
编辑器、浏览器等,显示Byte流时,需要遵照一个编码规则,如果使用的编码规则与实际编码不符,显示的东西就完全看不懂,俗称乱码。
- ASCII: 一套编码规则,为了让计算机能够识别字符而设计的,由美国制定、固定用1个字节来存放,规则比较简单,范围是0-127,每1个整数对应1个字符。
例1:十进制整数 65 对应字符 A。
- GB2312: 也是一套编码规则,由中国制定,固定用2个字节来存放。收录规则是“区位码”,第1个字节对应“区”,第2个字节对应“位”,区和位的范围都是1-94,计算时需要把区号和位号分别加上0xA0,按十进制算就是加160。
例2:字符A对应第3区的33位,那么分别加160后,就是163区的193位,转为16进制是A3和C1,即A3C1。
例3:字符“黄”对应第27区的38位,分别加160就是187区的198位,同理转为16进制,即为BBC6。
- Unicode: 同ASCII一样,只是它把所有语言都收录到一套编码里了,所以也叫万国码,通常使用2个字节来存放。它的编码规则和ASCII一样,就是顺着排,前128个字符就是ASCII码,之后是扩展码,不足2个字节的部分,用0补齐。
例4:字符A对应的数字还是 65,二进制是 00000000 01000001。(与ASCII不同的是不足2字节的部分补0)
例5:字符“黄”对应的数字是40644,二进制是 1001111011000100,十六进制是9EC4。
- UTF-8: 一套新的编码规则,特点可变长,使用1 - 4个字节表示一个字符。编码规则是根据Unicode对应的数字匹配到一个范围,字符对应关系还是顺着排。匹配范围如下(用十进制表示):
0-127用1个字节:0xxxxxxx
128-2047用2个字节:110xxxxx 10xxxxxx
2048-65535用3个字节:1110xxxx 10xxxxxx 10xxxxxx
65536-1114111用4个字节:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
编码时从左到右,根据范围来编码。解码时从右到左,0开头就是第1区间,110开头就是第2区间,1110开头就是第3区间,11110开头就是第4区间。
这里可以看出来,1区间(0-127)兼容了ASCII,这也是它使用最广泛的原因。
例6:字符A对应的数字是 65,65属于0-127区间,使用1个字节存放,二进制为01000001,和ASCII完全一样,所以它兼容ASCII。
例7:字符“黄“对应的unicode的数字是40644,属于2048-65535区间,所以需要3字节来存。对应的utf8的数字是15317892,十六进制是E9BB84。
从例6和例7可以看出,unicode的数字所在区间仅决定utf8编码占用的字节数,具体编码还是utf8自己编的。由此可知两者的关系:unicode被当作了utf8的区间索引。
例8:解码一个6字节的utf8的数据 \xe4\xb8\xad\xe6\x96\x87。开头是e4,转为2进制就是11100100,匹配1110开头,那就是3个字节。所以就连取3个字节\xe4\xb8\xad作为一个字符,就是e4b8ad,十进制是14989485,可以查到这是一个字符“中”。
接下来看下一个字节\xe6,e6的二进制是11100110,也是1110开头,匹配3个字节。所以取3字节就是e69687,十进制是15111815,对应匹配的字符是“文”。
最后可以得到这个6字节的utf8数据的解码后是“中文”2个字符。
最后总结一下它们的关系:
【ASCII】与【GB2312】:GB2312包含ASCII,编码毫无关系。
【ASCII】与【Unicode】:Unicode包含ASCII,重合部分,ASCII不足2字节的部分补0就是Unicode。
【ASCII】与【UTF-8】:UTF8包含ASCII,重合部分编码完全相同。
【GB2312】与【Unicode】:Unicode包含GB2312,编码毫无关系。
【GB2312】与【UTF-8】:UTF-8包含GB2312,编码毫无关系。
【Unicode】与【UTF-8】:Unicode是utf8的区间索引。编码部分,除了它们与ASCII重合的那部分,其它毫无关系。