前言
在日常的计算机文本操作中,经常会遇到一些字符编码上的问题,很多时候都是一知半解,默认就完事,也没有去仔细了解过。今天正好周末,做一个细致的整理,也是加深自己的理解。
1. 字符
字符是一种计算机数据类型。
因为计算机只能处理数字(二进制数,每一个二进制位有 0 和 1 两种状态), 我们要处理文本,就必须将文本转换成二进制数才能处理。文本中的内容一般为字符。最早的计算机在设计时采用8个比特 (bit) 作为一个字节(byte),一个字节能表示的最大整数就是255 (二进制 1111 1111 = 十进制 255)。
但0,1的这些数字又是如何转换成我们看到的文本呢?
2. ASCII
ASCII码,通俗的理解是数字与字符的一一映射关系表。映射关系对应如下图2-1。ASCII码包含了美国人常用的24个英文的大小字母,数字和一些特殊字符,并采用1字节(byte)来存储。
ASCII码一共规定了128个字符。这128个字符,只占用了一个字节的后面7位,最前面的一位统一规定为0 (二进制 0111 1111 = 十进制 127)。
现在问题出现了,按照ASCII码的标准,文本中只能出现ASCII码内包含的字符,其它语言怎么办,汉字又是如何显示的呢?
3. GB2312
GB2312是汉字字符集和编码的代号,中文全称为“信息交换用汉字编码字符集”,由中华人民共和国国家标准总局发布,一九八一年五月一日实施,适用于汉字处理、汉字通信等系统之间的信息交换,通行于中国大陆。GB2312基本集共收入汉字6763个和非汉字图形字符682个,收录的汉字已经覆盖中国大陆99.75%的使用频率。
GB2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。通俗的理解就是,在一个94*94的方格纸中,将收录的字符放入方格中,行为“区”,列为“位”。每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”(也称“区字节”),第二个字节称为“低位字节”(也称“位字节”)。具体如下图3-1。
举例来说,“啊”字是GB2312之中的第一个汉字,它的区位码就是1601(表示在16区,01位)。那么“啊”字在GB2312的编码应该是 10 01 (十六进制,主要是方便查看,当然在计算机底层还是二进制)?
这里GB2313的编码还是一个规定,区字节、位字节分别要加上A0(十六进制),“啊”字在GB2312的编码应该是 B0 A1(10+A0=B0, 01+A0=A1)。
“李”字的区位码为3278,那它在GB2313中的编码已应该是多少呢?大家有兴趣可以试下,也可以加深自己的理解。
介绍完GB2313后,大家可能会脑补一个场景:一个美国人,使用ASCII编码了一段文本,通过Email发给一个中国人,中国人打开Email,使用GB2313解码了这段文本,这是什么鬼,怎么全是乱码。当然,大家可能会说,怎么不用ASCII码来解码,这是一个办法。那有没有想过,如果是一个法国人或者俄国人发的Email,那怎么办,他们的编码方式也不一定会是ASCII,一种一种编码方式去试?这就显的很浪费时间了,有没有一种统一的编码方式,来包含所有的文字字符。于是Unicode应运而生。
4. Unicode
Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。目前的Unicode字符分为17组编排,00 00 00 至 10 FF FF(十六进制,00到10,正好17个数字),每组称为平面(Plane),而每平面拥有65536(十六进制,00 00 到 FF FF)个码位,共1114112个。
通过上面的定义,可以很清晰的看到Unicode采用三个字节来表示一个字符,那么所有计算机都采用Unicode来编码,之前乱码的问题也就解决了?当然是。那后来出现的UTF-8, UTF-16, UTF-32, UCS-2,UCS-4又是什么?
我看了一些资料,对这个问题也是一知半解,一般的解释是:Unicode是数字字符对应表,UTF-8, UTF-16, UTF-32, UCS-2,UCS-4,等等这些是Unicode的具体存储方式。我的理解是Unicode是我们脑海中想到的东西,UTF-8, UTF-16...是我们记在纸上的东西。下面只介绍下应用最广的UTF-8的内容。
5. UTF-8
UTF是“Unicode Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。
UTF-8以字节为单位对Unicode进行编码。从Unicode到UTF-8的编码方式如下:
具体解读规则如下:
- 对于单字节的字符,字节第一位设为0
- 对于n > 1的字符,第一个字节的前 n 位设为1,第 n+1 位设为0;其后字节前 2 位统一设为10
- 图中x代表字符的Unicode二进制数,不足填入0
举例说明:
- 英文字母“A”,对应的Unicode是 00 00 41(十六进制),对应上表第一行,为单字节,所以英文字母“A”的UTF-8的编码是 0100 0001(二进制)
- 中文字符“李”,对应的Unicode是 00 67 4E(十六进制),对应上表第三行,为三字节,所以中文字符“李”的UTF-8的编码是 1110 0110 1001 1101 1000 1110
结尾
写了一下午,算是没有虚度今天,也是自己对编码理解的一次梳理。同时也希望对有疑惑的朋友有点帮助。