ASCII、Unicode、UTF-8、UTF-16、GBK、GB2312、ANSI等编码方式简析

ASCII、Unicode、UTF-8、UTF-16、GBK、GB2312、ANSI等编码方式简析

序言

从各种字节编码方法中,能看到那个计算机发展的洪荒时期的影子。

ASCII

ASCII码有标准ASCII码和拓展ASCII码之分,这里分开讲解。

  1. 标准ASCII码

    标准ASCII码占用一个字节,但是只用了后7位,第一位是0.一个字节本来可以表示256种不同情况,而由此ASCII码只有128种符号。这128种符号包括英文26字母的大小写、数字0-9、32个不可打印的控制字母、符号(就是我们在键盘上可以看到的符号)

    附ASCII码表查询地址:http://ascii.911cha.com/

  2. 拓展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,它有几个显著的缺点:

  1. 不支持ASCII编码:由UTF-16的定义可以看出来,它的最短长度是16位,也就是2个字节,这就使得它无法兼容ASCII
  2. 相比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. 英文字符

字节数 : 1;编码:GB2312
字节数 : 1;编码:GBK
字节数 : 1;编码:GB18030
字节数 : 1;编码:ISO-8859-1
字节数 : 1;编码:UTF-8

字节数 : 2;编码:UTF-16BE
字节数 : 2;编码:UTF-16LE

字节数 : 4;编码:UTF-16

  1. 中文字符

字节数 : 1;编码:ISO-8859-1

字节数 : 2;编码:UTF-16BE
字节数 : 2;编码:UTF-16LE
字节数 : 2;编码:GB2312
字节数 : 2;编码:GBK
字节数 : 2;编码:GB18030

字节数 : 3;编码:UTF-8(大部分,很少部分需要4字节)

字节数 : 4;编码:UTF-16


Reference

  1. http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
  2. https://www.freebuf.com/articles/others-articles/25623.html
  3. https://www.cnblogs.com/malecrab/p/5300486.html
  4. https://www.zhihu.com/question/19677619
  5. 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
  6. https://juejin.im/post/5ace27c96fb9a028dc416195
  7. http://www.ruanyifeng.com/blog/2016/11/byte-order.html
  8. https://www.zhihu.com/question/62587928
  9. https://blog.csdn.net/a_little_a_day/article/details/78923071
    10.https://blog.csdn.net/yaomingyang/article/details/79374209
posted @ 2019-09-13 14:56  别再闹了  阅读(2395)  评论(0编辑  收藏  举报