从网上辛 ( sui ) 辛 ( sui ) 苦 ( bian ) 苦 ( bian )找来一份源码,用编辑器打开,发现注释各种乱码,这种情况相信不少人都遇到过。经过一番查找发现是编码格式的问题,OK,GB2312 / UTF-8 / Unicode 挨个试,分分钟解决,但是仔细想想,真的解决了么?
字符编码是什么?
我们知道,计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字,机器才能处理,而字符编码,就是将你所认识的各种字符类型转换为计算机所认识的字符类型的一种行为。
在计算机被设计出时,采用 8 个比特(bit)作为一个字节(byte),因此,一个字节能表示的最大整数就是255,如果想要表示更大的整数,就必须使用更多字节。比如,一个字节的最大二进制数 1111 1111 = 255
(十进制),两个字节的最大二进制数 1111 1111 1111 1111 = 65535
,以此类推。
而由于计算机是美国人发明的,他们没有需要以多个字节进行编码的文字,比如中文需要两个字节进行编码,英文、数字、符号只需要一个字节就够了,因此,最早的编码格式只包含 127 个字符,也就是大小写英文字母、数字和符号,这种编码被称为ASCII
编码,比如大写字母A
的编码是65
,小写字母a
的编码是97
。
GB2312
GB2312
是中国制定的编码,其目的就是解决ASCII
编码不适用于中文的问题,且不能和 ASCII
编码冲突。GB2312
是国内最早制定的汉字编码,目前最常用的是GBK18030
编码,另外还有GBK
编码。GBK18030
编码共收录 27484 个汉字,同时包含藏文、蒙文、维吾尔文等主要少数民族文字,现在Windows平台必须要支持 GBK18030
编码。
Unicode
我们可以想到的是,全世界上百种语言,日本有日文编码,韩国有韩文编码,各国有各自的标准,不可避免的会出现冲突,这就会导致一个问题,即在多语言混合的文本中,显示出来必然会有乱码。针对这一问题,产生了Unicode
编码,**它把所有语言都统一到一套编码里,这样就不会再有乱码问题。
大一统之后,乱码问题解决了,但是又有了新的问题。比如,采用Unicode
编码进行纯英文文档编辑,我们知道英文只需要一个字节就能满足编码,那么在Unicode
编码格式下,只需要在 ASCII 编码下英文的二进制数前面补 0 就可以了,例如A
的ASCII
编码是01000001
,对应Unicode
编码就是00000000 01000001
。这就导致存储成本的提升,对于全英文文本而言,Unicode
编码比ASCII
编码多出一倍的存储空间,在存储和传输上都十分不划算。
UTF-8
那么如何解决上述问题?一条合理的思路是,实现一套可伸缩的编码格式,即根据所编码字符的实际有效长度进行编码,进而出现了UTF-8
编码。UTF-8
编码把一个Unicode
字符根据不同的数字大小编码成 1-6 个字节,常用的英文字母被编码成 1 个字节,汉字通常是 3 个字节,只有很生僻的字符才会被编码成 4-6 个字节。
在这一编码格式下,如果你要传输的文本包含大量英文字符,用UTF-8
编码就能节省空间:
字符 | ASCII | Unicode | UTF-8 |
---|---|---|---|
A | 01000001 | 00000000 01000001 | 01000001 |
中 | x | 01001110 00101101 | 11100100 10111000 10101101 |
通过表格还可以发现,UTF-8 编码还有一个额外好处,就是 ASCII 编码实际上可以被看成是 UTF-8 编码的一部分,所以,大量只支持 ASCII 编码的历史遗留软件可以在 UTF-8 编码下继续工作。
总结
搞清楚什么是 ASCII、Unicode 以及 UTF-8 之后,我们总结一下字符编码的工作方式:
内存中统一使用Unicode
编码,当需要保存到硬盘或进行传输的时候,就转换为UTF-8
编码。
用记事本编辑时,从文件读取的UTF-8
字符被转换为Unicode
字符到内存中,编辑完成后,在保存时再将Unicode
转换为UTF-8
。