ASCII、Unicode、UTF-8、UTF-16、GBK、GB2312、ANSI等编码方式简析
ASCII、Unicode、UTF-8、UTF-16、GBK、GB2312、ANSI等编码方式简析
序言
从各种字节编码方法中,能看到那个计算机发展的洪荒时期的影子。
ASCII
ASCII码有标准ASCII码和拓展ASCII码之分,这里分开讲解。
-
标准ASCII码
标准ASCII码占用一个字节,但是只用了后7位,第一位是0.一个字节本来可以表示256种不同情况,而由此ASCII码只有128种符号。这128种符号包括英文26字母的大小写、数字0-9、32个不可打印的控制字母、符号(就是我们在键盘上可以看到的符号)
附ASCII码表查询地址:http://ascii.911cha.com/
-
拓展ASCII码(EASCII,即Extended ASCII)表
拓展ASCII码依然是一个字节,只不过把空闲下来的第一位也用上了。EASCII码比ASCII码扩充出来的符号包括表格符号、计算符号、希腊字母和特殊的拉丁符号
Unicode和UTF-8
划重点:Unicode并不是一种具体的实现方式,而只是为所有的字符标记一个对应的数字,并不限制具体如何实现。而utf-8恰好是Unicode最广为应用的实现方法
UTF-8的编码规则如下:
1)对于单字节的符号,字节的第一位设为
0
,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。2)对于
n
字节的符号(n > 1
),第一个字节的前n
位都设为1
,第n + 1
位设为0
,后面字节的前两位一律设为10
。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
根据这个规则,我们可以很轻易的解析出UTF-8的编码:如果一个字节的第一位是0
,则这个字节单独就是一个字符;如果第一位是1
,则连续有多少个1
,就表示当前字符占用多少个字节。
UTF-8实现了对ASCII码的向后兼容,并且对于各种符号的不同字节长度表示都给予了支持,所以被称为可变长度编码。这有一个显著的好处,就是对于纯英文文档,我们依然可以用一个字节来表示所有的字符(要知道,如果只能用两个字节来表示的话,所有英文文档的大小将是用ASCII码表示的两倍,这个代价所示不可接受的)
所以,如果我们仅仅是说字符是由utf-8编码的话,我们是不知道它的字节占用情况的~
GB2312和GBK等
GB2312是一种用两个字节表示汉字的编码方法。它的全称是“中华人民共和国国家标准简体中文字符集“,由中国国家标准总局发布,1981年5月1日实施。
GBK是对GB2312-80的扩展.由于GB 2312-80只收录6763个汉字,有不少汉字,如部分在GB 2312-80推出以后才简化的汉字(如“啰”),部分人名用字(如中国前总理***的“*”字),台湾及香港使用的繁体字,日语及朝鲜语汉字等,并未有收录在内。于是厂商微软利用GB 2312-80未使用的编码空间,收录GB 13000.1-93全部字符制定了GBK编码。
对于ASCII字符,gbk依然是用一个字符表示的,是兼容的
国家标准GB18030-2005《信息技术 中文编码字符集》是我国继GB2312-1980和GB13000-1993之后最重要的汉字编码标准.GB18030-2005的主要特点是在GB18030-2000*础上增加了CJK统一汉字扩充B的汉字。
目前使用较多的还是GBK和GB2312
ANSI
不同的国家和地区制定了不同的标准,由此产生了 GB2312、GBK、Big5、Shift_JIS 等各自的编码标准。这些使用 1 至 4 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。
在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。 不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。
由此可见,ANSI实际上指什么,有赖于操作系统的语言。这其实可以理解为微软(因为根据引用3所说,只有Windows系统使用ANSI编码)为了适应不同国家的编码方式的不同而想到的方法:在不同国家默认使用不同的编码方法,只是为了显示功能性的统一才起名为ANSI。个人认为这个是有点坑的设置····
那么ANSI的这个根据国家切换编码方式的功能是怎么控制的呢?
微软用一个叫“Windows code pages”(在命令行下执行chcp命令可以查看当前code page的值)的值来判断系统默认编码,比如:简体中文的code page值为936(它表示GBK编码,win95之前表示GB2312,详见:Microsoft Windows' Code Page 936),繁体中文的code page值为950(表示Big-5编码)。
这个code page也是可以控制的:
命令提示符下,我们可以通过chcp命令来修改当前终端的active code page,例如:
(1) 执行:chcp 437,code page改为437,当前终端的默认编码就为ASCII编码了(汉字就成乱码了);
(2) 执行:chcp 936,code page改为936,当前终端的默认编码就为GBK编码了(汉字又能正常显示了)。
上面的操作只在当前终端起作用,并不会影响系统默认的“ANSI编码”。
如果要修改全局的code page,就要设置当前系统区域(locate)
而对于Linux,其默认的编码方式是utf-8,如果我们在Windows下写好一个用ANSI编码方式(对于中国,其实就是GBK)写的文件,在Linux系统中打开,就会看到乱码。这时我们可以选择更改locale,即更换Linux的编码方式来解决问题,见图(图源引用3)
UTF-16LE 和UTF-16BE
这是记事本中的编码方式,还剩下UTF-16 LE和UTF-16 BE我们没有提到。
UTF-16
UTF-16是Unicode字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为 "storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即码元, 长度为2 Byte)的序列,用于数据存储或传递。Unicode字符的码位,需要1个或者2个16位长的码元来表示,因此这是一个变长表示。
UTF-16的出现时间比UTF-8早,但是相比UTF-8,它有几个显著的缺点:
- 不支持ASCII编码:由UTF-16的定义可以看出来,它的最短长度是16位,也就是2个字节,这就使得它无法兼容ASCII
- 相比UTF-8更费空间
现今UTF-16依然存在于计算机系统中是为了向下兼容,其本身用的已经不太多了(但也有应用,比如在JavaScript中,所有的string类型(或者被称为DOMString)都是使用UTF-16编码的),所以详细的编码方法这里就不说了。
LE 和BE是什么
LE是指低字节序(Little Endian),BE指高字节序(Big Endian),这里涉及到了一个很底层的问题:如果一个字符需要两个字节来表示,那么这两个字节应该怎么存储,是高位在前还是低位在前?这个问题在当初学习汇编语言时就曾给我造成疑惑,但当时的我并不知道这背后的故事。
Endian读作End-ian或者Indian。这个术语的起源可以追溯到格列佛游记。(小说中,小人国为水煮蛋应该从大的一端(Big-End)剥开还是小的一端(Little-End)剥开而争论,争论的双方分别被称为“大端派”和“小端派”。)
字节序方案只是一个微处理器架构设计者的偏好问题,例如,Intel使用低字节序,Motorola使用高字节序。
以汉字
严
为例,Unicode 码是4E25
,需要用两个字节存储,一个字节是4E
,另一个字节是25
。存储的时候,4E
在前,25
在后,这就是 Big endian 方式;25
在前,4E
在后,这是 Little endian 方式。
可以看到,BE方式和人类读写数值的方式是一致的,那为什么不直接用BE?
计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序
我们可以注意到一个问题,上面图中为什么只有UTF-16有字节序问题,为什么ANSI和UTF-8没有字节序问题?不是因为UTF-16特别,是因为Unicode在制定标准的时候,并没有规定字节序,这就给UTF-16和UTF-32这样的以多字节为处理单位的编码方式埋了一个大坑,使得后代许多人都纠结在字节序转换的问题上。而UTF-8是以单字节为*本处理单位的
如果一个字符使用utf-8表示,就需要将这个字符的Unicode码,编码成字节数组,这里要注意“字节”“数组”这两个概念,所以对于字节数组写入内存时,只需要按照数组的顺序,一个一个字节写入,不存在高位和低位的问题。
而对于GBK,机智的标准制定者在标准中已经说明白了:
划重点:两个字节中前面的字节为第一字节,后面的字节为第二字节
如何处理字节序问题
有一个很巧妙的约定来解决UTF-16的字节序问题,它叫做BOM(字节顺序标记)。BOM放置在文档的开头,用来告诉阅读器这个文档的字节序是什么。如果是高字节序,就写入FE FF;如果是低字节序,就写入FF FE。
"带有BOM的UTF-8"又是什么?
UTF-8 不需要 BOM,尽管 Unicode 标准允许在 UTF-8 中使用 BOM。
所以不含 BOM 的 UTF-8 才是标准形式,在 UTF-8 文件中放置 BOM 主要是微软的习惯(顺便提一下:把带有 BOM 的小端序 UTF-16 称作「Unicode」而又不详细说明,这也是微软的习惯)。微软在 UTF-8 中使用 BOM 是因为这样可以把 UTF-8 和 ASCII 等编码明确区分开,但这样的文件在 Windows 之外的操作系统里会带来问题。例如UTF-8 的网页代码不应使用 BOM,否则常常会出错。
总结
这里附上一个很好用的表,便于涉及到对应的处理时进行查询(来源:引用10)
占用字节从小到大排序
- 英文字符
字节数 : 1;编码:GB2312
字节数 : 1;编码:GBK
字节数 : 1;编码:GB18030
字节数 : 1;编码:ISO-8859-1
字节数 : 1;编码:UTF-8字节数 : 2;编码:UTF-16BE
字节数 : 2;编码:UTF-16LE字节数 : 4;编码:UTF-16
- 中文字符
字节数 : 1;编码:ISO-8859-1
字节数 : 2;编码:UTF-16BE
字节数 : 2;编码:UTF-16LE
字节数 : 2;编码:GB2312
字节数 : 2;编码:GBK
字节数 : 2;编码:GB18030字节数 : 3;编码:UTF-8(大部分,很少部分需要4字节)
字节数 : 4;编码:UTF-16
Reference
- http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
- https://www.freebuf.com/articles/others-articles/25623.html
- https://www.cnblogs.com/malecrab/p/5300486.html
- https://www.zhihu.com/question/19677619
- https://zh.wikipedia.org/wiki/%E6%B1%89%E5%AD%97%E5%86%85%E7%A0%81%E6%89%A9%E5%B1%95%E8%A7%84%E8%8C%83
- https://juejin.im/post/5ace27c96fb9a028dc416195
- http://www.ruanyifeng.com/blog/2016/11/byte-order.html
- https://www.zhihu.com/question/62587928
- https://blog.csdn.net/a_little_a_day/article/details/78923071
10.https://blog.csdn.net/yaomingyang/article/details/79374209