ASCII码表
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准ISO/IEC 646。
ASCII码使用指定的7位或8位二进制数组合来表示128或256种可能的字符。美国一开始设计的是128位,包含95个可见字符和33个控制字符,后期扩展到8位,称为扩展ASCII码。
国标编码
中国也有自己的编码,用于表示中文字符。设计编码首先要确定字符集。国标使用分区管理,共计分为94个区,每个区含94位,共8836个码位。19区收录除汉字外的682个字符;1015区为空白区,没有使用;1655区收录3755个一级汉字,按照拼音排序;5687区收录3008个二级汉字,按照部首/笔画排序;剩余的88~94区为空白区,没有使用。这套字符集称为GB2312字符集。按照这个字符集来编码,比如3区的6行7列字符,编码0367。
ASCII是直接把码位按照二进制存储,但国标编码不是。比如5709编码的汉字(侃),把6709按照前两位和后两位分开,并分别转为16进制:0x39和0x09;然后两个数分别加上0xA0得到0xD9和0xA9,将两数合并,0xD90xA9就是汉字侃的GB2312码。
GB2312编码之所以要加0xA0,是为了要兼容ASCII码。GB2312是双字节编码,为了与ASCII编码区分,每个单字节的第八位必须是1,所以GB2312编码最少要从0x80(1000 0000)开始。但是根据规定,0x80~0x9F需要留给控制块,所以只能从0xA0开始。
计算机判断字符是ASCII码还是GB2312编码的方式就是通过字符大小,ASCII码是7个字节,如果小于127就是ASCII码,如果连续碰到两个大于127的8位就把两个组合起来当作GB2312编码。
GB2312编码只能表示6763个汉字,后期也不够用了,所以对GB2312做了扩展为GBK字符集。GB2312的高位和地位都要求必须大于127,GBK不再规定低位大于127,只保证高位大于127。把之前没用上的码位都用上,扩充了将近20000个汉字和符号。计算机只要碰到一个大于127的字节,就表示一个汉字的开始。
后期还扩展了GB18030字符集,新增了少数民族的字符。
Unicode
ASCII可表示的字符数太少了,但如果每个国家都设计一套自己的编码就太乱了,为了把世界上的文字都映射到一套字符空间中,诞生了Unicode。Unicode是一个标准,规定了字符集和编码。
最开始的Unicode字符集称为UCS-2字符集,和ASCII码一样,把用到的字符按顺序罗列并标上对应的码位。存储方式和ASCII一样,直接把码位按照二进制方式存储。一共可以表示
互连网时代后,对Unicode做了优化,目前有3种Unicode的编码方式:
- UTF-8:用1byte来表示字符,可以兼容ASCII码;
- 特点是存储效率高,可变长(不方便内部随机访问);
- 无字节序问题,可以作为外部编码;
- UTF-16:用2bytes表示一个字符,可以分为UTF-16BE(big
endian)和UTF-16LE(little endian)
- 特点是定长的,方便内部随机访问;
- 有字节序问题,不可以作为外部编码;
- UTF-32:用4bytes表示一个字符,可以分为UTF-32BE(big
endian)和UTF-32LE(little endian)
- 特点是定长的,方便内部随机访问;
- 有字节序问题,不可以作为外部编码;
注意:如果使用的Windows系统,Windows文件可能有BOM(byte order mark)来表示字节序,如果要在其他平台使用,可以去掉BOM或者忽略掉。BOM在文本文件头部,FEFF(十进制为254 255)表示大端,FFFE(十进制为255 254)表示小端。
UTF-8编码原理:UTF-8将UCS-4字符集的码位划分为4个区间(0x000000000x0000007F;0x000000800x000007FF;0x000008000x0000FFFF;0x000100000x0010FFFF),第一个区间的编码样式为0XXXXXXX,第二个区间的编码样式为110XXXXX 10XXXXXX,第三个区间的编码样式为1110XXXX 10XXXXXX 10XXXXXX,第四个区间的编码样式为11110XXX 10XXXXXX 10XXXXXX 10XXXXXX。
汉字“王”在UCS-4中的编码为0x0000738B,转换为二进制就是“0000 0000 0000 0000 0111 0011 1000 1011”。0x0000738B属于UCS-4的第三区间,这个区间的编码样式是“1110XXXX 10XXXXXX 10XXXXXX”。此时我们得到了“王”这个字符的二进制形式和编码样式,把二进制形式从高到低依次插入到编码样式中,得到了“王”对应的UTF-8编码:“11100111 10001110 10001011”,十六进制为“0xe7 0x8e 0x8b”。
程序中编码错误的根本原因在于编码方式和解码方式的不统一。