ASCII
上个世纪60年代,美国制定了基于拉丁字母的一套电脑编码系统,取名为ASCII。它主要用于显示现代英语和其他西欧语言,是现今最通用的单字节编码系统。
ASCII码使用指定的7位或8位二进制数组合来表示128或256种可能的字符。标准ASCII码也叫基础ASCII码,使用7位二进制数来表示所有的大写和小写字母,数字0到9、标点符号,以及在美式英语中使用的特殊控制字符。
其中:0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(响铃)等;通信专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等;ASCII值为8、9、10 和13 分别转换为退格、制表、换行和回车字符。它们并没有特定的图形显示,但会依不同的应用程序,而对文本显示有不同的影响。
32~126(共95个)是字符(32是空格),其中48~57为0到9十个阿拉伯数字,65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。
同时还要注意,在标准ASCII中,其最高位(b7)用作奇偶校验位。所谓奇偶校验,是指在代码传送过程中用来检验是否出现错误的一种方法,一般分奇校验和偶校验两种。奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1;偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1。
后128个称为扩展ASCII码。许多基于x86的系统都支持使用扩展ASCII。扩展ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号。
GB2312
时代在进步,电脑得到了普及,但是因为咱们中国的文化博大精深,而ASCII码又恰恰无法满足这么多文字的表示。所以在ASCII码的基础下,咱们自己走出了一个编码的新分支。
1980年,中国国家标准总局年发布了《信息交换用汉字编码字符集》,取名为GB2312标准,其中规定:用两个字节来表示一个汉字,分别为高字节(也称“区字节” 取值范围0xA1-0xF7)和低字节(也称“位字节” 取值范围0xA1-0xFE)。高字节大于127,这样就兼容了ASCII码。
GB2312大约可以组合出7000多个常用汉字,并且在这些编码里,数学符号、罗马字母、日文的假名等都编进去了,甚至 ASCII 里原本就有的数字、标点、字母都重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127以下的那些就叫"半角"字符了。
总之一句话,GB2312 是对 ASCII 码的中文扩展,与后面说到UNICODE和UTF8基本没有联系。题外话:不知道有么有人记得,前些年重要考试时,可能需要先查找自己名字的区字节和位字节,进行相应的涂卡才能考试,有些人的名字比较生僻,就悲催了。
GBK
继续在咱们自己的标准分支上前进,GB2312只解决了大多数常用文字,所以继续规定:只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容,扩展之后的编码方案被称为GBK标准,GBK包括了GB2312的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号.
UNICODE
重点来了,因为当时各个国家都像中国这样搞出了一套自己的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,正所谓:统一是大势所趋,地球正在逐渐变小。
国际标准化组织(ISO)决定插手了,解决办法就是废弃所有的地区性编码方案,重新统一编写包含所有文化、字母和符号的编码,于是在1994年正式发布了统一编码标准,取名UNICODE(Universal Multiple-Octet Coded Character Set",简称 UCS,万国码),它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。
UNICODE规定用两个字节,也就是16位来统一表示所有的字符。对于ASCII码里的那些“半角”字符,UNICODE保持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。在UNICODE中,一个字符就是两个字节。
需要注意的是,Unicode只是一个符号集,它只规定了符号对应的二进制代码,却没有规定这个二进制代码应该如何存储和传输,所以市面上有许多关于Unicode编码存储和传输的实现方式,比如UTF-8,UTF-16等等。
实现方式就是一种规则。汉字”严”的Unicode16进制码为4E25,转换成二进制数为(0100 1110 0010 0101),通过制定一定的规则,实际存储中,汉字“严”在内存中就可能表示为(0101 0001 1101,这个是随便写的)。但不管是怎样的转换,计算机都会根据这个规则,将(0101 0001 1101)识别为汉字“严”,可能说的有点绕,只可意会不可言传啊。UTF-8就是互联网上使用最广的一种实现Unicode的方式和规则。
UTF-8
UTF(UnicodeTransformationFormat,Unicode的转换格式)是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
下表总结了编码规则,字母x表示可用编码的位。
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
-----------------------------------------------------------------------------
跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
下面,还是以汉字"严"为例,演示如何实现UTF-8编码。
已知"严"的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此"严"的UTF-8编码需要三个字节,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然后,从"严"的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,"严"的UTF-8编码是"11100100 10111000 10100101”(红字合成后就是unicode编码),转换成十六进制就是E4B8A5。
引申
存储和传输就必须讨论“大端模式”和“小端模式”,只有保证解读数据一致性,才能确保不出现数据错误和乱码。
大端模式(Big_endian):数据的高字节存储在低地址中,而数据的低字节则存放在高地址中。
小端模式(Little_endian):数据的高字节存储在高地址中,而数据的低字节则存放在低地址中。
Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做"零宽度非换行空格"(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。
如果一个文本文件的头两个字节是FEFF,就表示该文件采用大端方式;如果头两个字节是FFFE,就表示该文件采用小端方式。
PS:参考阮一峰著作,感兴趣的朋友,请点这里。