注:本文根据阮一峰老师的一篇文章所写,原文链接:点我 。旨在帮助大家了解编码方面的一些知识,如有错误,恳请指正。
一、ASCII 码
ASCII码划分为两个集合:128个字符的标准ASCII码(7位二进制编码)和附加的128个字符的扩展ASCII码(8位二进制编码)。
电脑的最小数据存储单位是字节(Byte),1 Byte = 8 bit 。由于早期的电脑不太可靠,标准ASCII码的最高位(b7)就拿来用作奇偶校验位。奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1;偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1。
后来电脑变得可靠了,校验位就没有什么意义了,因此有了拓展ASCII码字符集。扩展ASCII字符集中的前128个字符与标准ASCII字符集相同(就是标准ASCII字符集的7位编码前面加一个0),而后面128个字符最高位都是1。
二、ASCII 码的不足
英语用128个符号就基本足够了,但是其他语言,比如法语、俄语等,是不够的,只能使用拓展ASCII码(256个字符)来表示,但是这又引出了一个新问题,后128种编码在不同的语言中所代表的字符是不一样的。例如,130在法语编码中代表é,在希伯来语编码中却代表字母Gimel (ג) 。
对于汉字来说,有将近10万个,1个字节所能表示的256种字符肯定是不够了,只能多字节来表示。如常见的GB2312编码标准采用双字节编码,理论可表示256 x 256 = 65536个符号。汉字编码还有BIG5、GBK、GB18030等,本文不再赘述。
编码方式如此之多,如果编码和解码的时候采用的方式不同,则会造成乱码。如果有一种编码,把世界上所有的符号都包含了进去,万物统一,那岂不是再也不用担心出现乱码的情况了。Unicode应运而生。
三、Unicode
Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode字符集可以简写为UCS(Unicode Character Set)。早期的Unicode标准有UCS-2、UCS-4的说法。UCS-2用两个字节编码,UCS-4用4个字节编码(这种定长的编码方式都不够完美,UCS-2所包含的字符不全,UCS-4又浪费了存储空间。需要注意,UCS-2不应该被等同于UTF-16,而UCS-4等同于UTF-32)。
Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符。我们将0x10FFFF转换成二进制为100001111111111111111,共21位,也就是说需要3个字节才能表示这个字符,但比较靠前的字符只用1个字节或2个字节就够了。那么问题来了,计算机怎么知道你这三个字节是代表几个字符呢?可以代表1个字符,也可以代表2个字符,还可以代表3个字符,直接给计算机整懵逼。如果我们规定,所有字符统一用3个字节来表示(这和UCS-2、UCS-4没有本质的区别),这确实解决了上述问题,但显而易见的是,这种方案会导致文件的体积暴涨。
UTF-8出现了,这是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。UTF-8牛逼在以下3点:
1、它可以用来表示Unicode标准中的任何字符
2、由于是变长的,不会浪费太多存储空间
3、编码中的第一个字节仍与ASCII相容
四、UTF-8
首先要明确一点,UTF-8、UTF-16、UTF-32均为Unicode的实现方式之一(其中-8和-16是变长,-32是定长,目前公认的最优设计是UTF-8)。UTF-8 的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
| Unicode编码 | UTF-8编码(二进制) |
|---|---|
| U+0000 – U+007F | 0xxxxxxx |
| U+0080 – U+07FF | 110xxxxx 10xxxxxx |
| U+0800 – U+FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
| U+10000 – U+10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
举个例子,‘洁’ 这个字的Unicode编码为6D01(二进制为110110100000001),对应上表中的第三行,代表这个字在UTF-8编码中需要用3个字节存储,将对应的二进制填入第三行中的 x,多出的位补0,得到11100110 10110100 10000001,转化成十六进制为E6B481,这就是 ‘洁’ 字的UTF-8编码。顺道推荐一个好用的在线编码工具:超好用的编码工具 。
五、Little endian 和 Big endian
大家有时候会看到UTF-16BE、UTF-16LE 和 UTF-16,意义如下:
- UTF-16BE Sixteen-bit UCS Transformation Format, big-endian byte order
- UTF-16LE Sixteen-bit UCS Transformation Format, little-endian byte order
- UTF-16 Sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order mark(BOM)
UTF-16BE代表大端字节序,即将高位的字节存储在起始地址。
UTF-16LE代表小端字节序,即将低位的字节存储在起始地址。
UTF-16无指定后缀,代表字节序由起始的两个字节决定,FE FF(零宽非中断空格)表示大端,FF FE表示小端。
那为什么我们要关注字节序的问题?这是由于如果不同的程序在存储数据的时候采用的方式不同,那么在数据交换的时候就会发生错误。这很容易理解,比如 ‘洁’ 字大端存储为6D01,若以小端方式解析成016D,就会变成 ‘ŭ’ 这个符号,含义完全就变了。而UTF-8不存在字节序要求,这也是优势之一。
六、总结
UTF-8除了上述优点外,还有一个优势,边界明确、容错率高。在中间丢失了一段编码的时候,影响面最小。
不过UTF-8也是有一定缺点的。第一点是UTF-8编码中文字符需要3个字符,所以对于纯中文文本来说UTF-16或者GB2312等编码明显更优;第二点是在统计字符数量还是执行索引操作上的效率都不算高。
但我认为UTF-8的优势完全掩盖了它的不足,在将来,一统天下应该是没有问题的。
241

被折叠的 条评论
为什么被折叠?



