1.ASCII(American Standard Code for Information Interchange)
在学习C语言中,我们接触了最基本的一种编码格式:ASCII码。在计算机发明之初,一群美国人在心里琢磨,我们要在计算机上能显示我们的语言,我们的文字,就必须要把所有的英语字母,符号以及阿拉伯数字用一些二进制序列表示出来。这样,计算机看到的是二进制的编码,对应的是人能看懂的语言。一个符号对应一个号码。于是,美国人用一个字节(8 bit)来表示他们平时需要用到的字母,符号,数字等,1字节共有256种组合(00-FF)由于英语字母大小写一共只有52个,常用符号也有限,再加上一些常用的控制符,美国人只用了其中的128个(0-127)来编码,这就是最基本的ASCII码。(只用了128个,在上一次关于Byte的讨论中,我们应该能知道,这说明ASCII在8 bit的第八位永远是0)。
后来,计算机发展很快,全世界很多地方人都不使用英语。于是,他们果断利用了ASCII未使用的剩下的128个字符,来表示他们的字母,符号。大家都感觉这样很好。
2.GB(国标码)
后来中国人开始用计算机,中国人发现,我们已经没有可以利用的字符编码来表示汉字了。况且,中国常用字就有6000多个,1 Byte只有256种组合,这根本就是供不应求啊。但这难不倒中国人,我们果断把1 Byte中的后128个字符去掉,前128个沿用ASCII字符。并且我们用两个字节表示一个汉字。这两个字节的值都大于127。中国人感觉这样不错,于是我们把这种编码方案叫做“GB2312”,在GB2312中,我们也把英语字母,数字等编了进去。这样,同一个字母如果用GB码表示我们称作"全角"字符,如果用ASCII码表示,就是"半角"字符。输入法中有一个圆月和缺月的切换,指的就是全角字符和半角字符的切换。显示的效果也是完全不同的。
后来我们感觉汉字太多了,而且包括很多繁体字,于是我们毫不犹豫地规定,只要2字节字符编码的第一个字节大于127,就表示这个字节和后一个字节共同表示一个汉字,即使后一个字节是小于128的。这样,我们有了128*256个空间可以表示汉字,我们不仅加入了繁体字,还加入的少数名族的符号。我们把这种方案叫做GBK编码,后来GBK又被扩展成了GB18030。现在,大家应该知道了"为什么一个汉字算两个英文字符?为什么一个汉字包含两个字节?"了吧。
3.Unicode(Universal Multiple-Octet Coded Character Set)
如中国一样,全世界很多国家都开发了一套属于自己的字符编码方式,这就带来了一个问题,当一台计算机需要切换使用不同的语言系统时,他们如何区分这些不同的编码方案带来的字符冲突问题呢?在GB码中的汉字在别的编码中表示的也许是别的符号。
这时,一个很权威的组织站了出来,他就是ISO——国际标准化组织,他们制定了很多标准以解决这些冲突问题,这次也不例外。他们的方法是这样的,废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号的编码!他们叫它"Universal Multiple-Octet Coded Character Set",简称 UCS,俗称 "UNICODE"。Unicode用2字节的空间表示一个字符,一共可以表示65536个字符,这是UCS-2方案,为了防止不够用,ISO还制定了UCS-4方案,也就是用4个字节表示一个符号。这样编码得到的字符恐怕是用不完了吧。说到这儿我想插一句,经常我们打开一个文本文件或者一封邮件时,会看到一些乱码,看不懂的符号,原因就是因为文件保存与打开(邮件发送与接收)的编码方式不同,那么计算机当然就把原来的编码翻译成了我们看不懂的符号了。
4.UTF(UCS Transfer Format)
UTF是Unicode的一种实现形式,在互联网中应用十分广泛。UTF-8是应用最广的实现方式,其他的实现方式还包括UTF-16和UTF-32。通过UTF的名字我们也能理解出,UTF是为了适应比特流在网络中传输而产生的标准,UTF-8就是每次传输8位,UTF-16就是每次传输16位,UTF-32就是32位。那么现在我们来深入讨论一下Unicode和UTF-8之间是如何转换的。
(1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
(2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
具体点说:
Unicode | UTF-8编码方式
(十六进制) | (二进制)
---------------------+-----------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
举个例子,“严”的unicode是4E25(100 1110 0010 0101二进制),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此“严”的UTF-8编码需要三个字节,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,从“严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严”的UTF-8编码是“11100100 10111000 10100101”,转换成十六进制就是E4B8A5。
每次谈及编码必提一个著名的Bug,就是在Windows系统下,新建记事本,只写入“联通”二字,保存,关闭再打开会发现显示乱码,为什么呢?出现乱码我之前说过一定是文件内容的保存与打开的编码方式不同。
记事本的默认编码方式是ANSI,在ANSI下敲入中文时用的是GB2312编码,“联通”在GB2312下的字符码是C1AA CDA8
11000001 10101010 | 11001101 10101000
可以看出,这一段内码的两个部分正好都符合UTF-8的二字节格式,因此记事本会自动以UTF-8读取文本,将其转换成Unicode码,而第二部分转换成Unicode后不表示任何符号,因此无法正常显示。