写在前面:工作中遇到了中文显示的问题,程序中时不时会出现中文乱码,经过不断学习,终于明白了常见的几种编码格式,总结一下,分享给大家一起学习进步。
ASCII (American Standard Code for Information Interchange,美国信息交换标准代码)是由美国国家标准学会(American National Standard Institute , ANSI )制定的是一种标准的单字节字符编码方案,用于基于文本的数据。它最初是美国国家标准,供不同计算机在相互通信时用作共同遵守的西文字符编码标准,后来它被国际标准化组织(International Organization for Standardization, ISO)定为国际标准,称为ISO 646标准。适用于所有拉丁文字字母。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年。
标准ASCII 码也叫基础ASCII码,使用7 位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到9、标点符号,以及在美式英语中使用的特殊控制字符。
后128个称为扩展ASCII码。许多基于x86的系统都支持使用扩展(或“高”)ASCII。扩展ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号。1981年发布的扩展集。
GB全称GuoBiao国标,GBK全称GuoBiaoKuozhan国标扩展。
GB18030编码兼容GBK,GBK兼容GB2312,其实这三种编码有着非常深厚的渊源。
【GB2312】最早一版的中文编码是1980年发布,每个字占据2bytes。由于要和ASCII兼容,那这2bytes最高位不可以为0了(否则和ASCII会有冲突)。在GB2312中收录了6763个汉字以及682个特殊符号,已经囊括了生活中最常用的所有汉字。(GB2312编码全表:http://tools.jb51.net/table/gb2312)GB2312编码表有个值得注意的点,这个表中也有一些数字和字母,与ASCII里面的字母非常像。例如A3B2对应的是数字2(如下图),但是ASCII里面50(十进制)对应的也是数字2。他们的区别就是输入法中所说的“半角”和“全角”。全角的数字2占两个字节。通常,我们在打字或编程中都使用半角,即ASCII来编写数字或英文字母。特别是编程中,如果写全角的数字或字母,编译器很有可能不认识……
表:GB2312与ASCII重合的部分字符
【GBK】由于GB2312只有6763个汉字,我汉语博大精深,只有6763个字怎么够?于是GBK中在保证不和GB2312、ASCII冲突(即兼容GB2312和ASCII)的前提下,也用每个字占据2bytes的方式又编码了许多汉字。经过GBK编码后,可以表示的汉字达到了20902个,另有984个汉语标点符号、部首等。值得注意的是这20902个汉字还包含了繁体字,但是该繁体字与台湾Big5编码不兼容,因为同一个繁体字很可能在GBK和Big5中数字编码是不一样的。(GBK编码全表:http://tools.jb51.net/table/gbk_table)GBK1.0中华人民共和国全国信息技术标准化技术委员会1995年12月1日制订。
【GB18030】然而,GBK的两万多字也已经无法满足我们的需求了,还有更多可能你自己从来没见过的汉字需要编码。这时候显然只用2bytes表示一个字已经不够用了(2bytes最多只有65536种组合,然而为了和ASCII兼容,最高位不能为0就已经直接淘汰了一半的组合,只剩下3万多种组合无法满足全部汉字要求)。因此GB18030多出来的汉字使用4bytes编码。当然,为了兼容GBK,这个四字节的前两位显然不能与GBK冲突(实操中发现后两位也并没有和GBK冲突)。2000年 3月17日信息产业部和国家质量技术监督局联合发布 GB18030编码标准 ,2005年的是在2000年基础上进一步补充。至此,GB18030编码的中文文件已经有七万多个汉字了,甚至包含了少数民族文字。。
表:几种不同编码的前2字节值域
这图中展示了前文所述的几种编码在编码完成后,前2个byte的值域(用16进制表示)。每个byte可以表示00到FF(即0至255)。ASCII编码由于是单字节,所以没有第2位。因为GBK兼容GB2312,所以理论上上图中GB2312的领土面积也可以算在GBK的范围内,GB18030也同理。上图只是展示出了比之前编码“多”出来的面积。GB18030由于是4bytes编码,上图只是展示了前2bytes的值域,虽然面积最小,但是如果后2bytes也算上,GB18030新编码的字数实际上远远多于GBK。
可以看出为了做到兼容性,以上所有编码的前2bytes做到了相互值域不冲突,这样就可以允许几种不同编码中的文字同时出现在同一个文本文件中。只要全都按照GB18030编码的规则去解析并展示文件,就不会有乱码出现。
实际业务中GB18030很少提到,通常GBK见得比较多,这是因为如果你去看一下GB18030里面所编码的文字,你会发现自己一个字也不认识……
表:GB18030编码的部分汉字
Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。
Unicode 标准 15.0版已于2022-9-13发布,带来了新加的4489个字符,总计达到了149186个字符。
Unicode 为世界上所有字符都分配了一个唯一的数字编号,这个编号范围从 0x000000 到 0x10FFFF (十六进制),有 110 多万,每个字符都有一个唯一的 Unicode 编号,这个编号一般写成 16 进制,在前面加上 U+。例如:
U+9A6C表示汉字马,
U+4E25表示汉字严
U+0639表示阿拉伯字母Ain,
U+0041表示英语的大写字母A,
Unicode 就相当于一张表,建立了字符与编号之间的联系,Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
图:Unicode发展
Unicode的存储方式
最后的结果:
UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。
其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。
注意:UTF-8 是 Unicode 的实现方式之一。
###4.1 UTF-8 特点
UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
###4.2 UTF-8 的编码规则
UTF-8 的编码规则有二条:
**规则一:**对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
**规则二:**对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
下表总结了编码规则,字母x表示可用编码的位。
编号范围(编号对应十进制数) |
二进制格式 |
十六进制范围:(0x00—0x7f) ,十进制范围:(0—127) |
0XXXXXXX |
十六进制范围:(0x80—0x7ff) ,十进制范围:(128—2047) |
110XXXXX 10XXXXXX |
十六进制范围:(0x800—0xffff) ,十进制范围:(2048—65535) |
1110XXXX 10XXXXXX 10XXXXXX |
十六进制范围:(0x10000—0x10ffff) ,十进制范围:(65536以上) |
11110XXX 10XXXXXX 10XXXXXX 10XXXXXX |
BOM(Byte order mark)字节序标记
每个文件最前面(Unicode规定)加入一个字符,表示编码格式,这个字符叫做“零宽度非转换空格”
FE FF:UCS-2, Big endian
FF FE: UCS-2, Little endian
EF BB BF:UTF8
记事本另存为的编码:
ANSI:英文ASCII,中文GB2312(windows简体中文)
UTF16 LE:UCS-2编码,2个字节表示一个字符,并且使用little endian格式,这是windows的默认格式。
UTF16 BE:与UTF16 LE相比,使用Big endian格式
UTF8(带BOM):前三个字节EF BB BF,后面为UTF8编码
记事本 |
Notepad++ |
|
格式 |
"中流" |
格式 |
ANSI |
D6 D0 C1 F6 |
ANSI |
UTF16 LE |
FF FE 4E 2D 6D 41 |
UTF-16 LE BOM |
UTF16 BE |
FE FF 2D 4E 41 6D |
UTF-16 E BOM |
UTF8 |
E4 B8 AD E6 B5 81 |
UTF-8 |
UTF8 BOM |
EF BB BF E4 B8 AD E6 B5 81 |
UTF-8 BOM |
表:几种编码格式举例
ANSI编码
ANSI是一种字符代码,为使计算机支持更多语言,通常使用 0x00 ~ 0x7F范围的1 个字节来表示 1 个英文字符。超出此范围使用0x80~0xFFFF来编码,即扩展的ASCII编码。不同的国家和地区制定了不同标准,由此产生了 GB2312、GBK、GB18030、Big5、Shift_JIS 等各自的编码标准。这些使用多个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。
Unicode和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,转换成十六进制就是E4B8A5