字符编码(GB 2312、GBK、UTF-8、UTF-16)

GB 2312

GB 2312 是 1980 年发布的中文编码,共收录 7445 个字符,有 6763 个汉字以及 682 个非汉字字符,其中一级汉字 3755 个,二级汉字 3008 个。

GB 2312 采用双字节编码,两字节最高位均为 1,所以可以兼容 ASCII 码。

整个字符集分为 94 个区,每个区有 94 个位,7445 个字符被填入到这 94*94 个区位中。
每个区位上只有一个字符,因此可用所在的区和位来对汉字进行编码,称为区位码。区位码加上 0x2020 就得到国标码。国标码再加上 0x8080 就得到了两字节的计算机内码。

1 和 94 对应的 16 进制分别为 0x01 和 0x5E。
所以区位码的范围是:0x0101~0x5E5E,加上 0x2020
得到国标码的范围是:0x2121~0x7E7E,加上 0x8080
得到内码的范围是:0xA1A1~0xFEFE

在 GB 2321 中:
1 到 9 区,为非汉字字符。
10 到 15 区,没有字符。
16 到 55 区,为一级汉字,按拼音排序。
56 到 87 区,为二级汉字,按部首/笔画排序。
88 到 94 区,没有字符。

所以对应到内码:
0xA1A1~0xA9FE 为非汉字字符。
0xAAA1~0xAFFE 无编码。
0xB0A1~0xF7FE 为汉字。
0xF8A1~0xFEFE 无编码。

GB 2312 编码表参考:
http://doc.chacuo.net/gb2312

GBK

GBK 即汉字内码扩展规范,共收录 21886 个汉字和非汉字字符。

兼容 GB 2312,同样采样双字节编码,同样兼容 ASCII 码,与 GB 2312 不同的是,它只要求第一个字节最高位为 1。 解码时,遇到首位为 1 的字节,就连同下一个字节一起表示一个 GBK 编码,遇到首位为 0 的字节,就直接按 ASCII 码处理。

GBK 编码范围为 0x8140~0xFEFE,第一个字节在 0x81~0xFE 之间,第二个字节在 0x40~0xFE 之间,剔除了第二个字节为 0x7F 的编码。

GBK 编码表参考:
http://doc.chacuo.net/gbk

GB 18030(较少用)

GB 18030 兼容 GBK 和 ASCII,共收录汉字 70244 个,采用一二四字节可变长编码。

Unicode

Unicode 是一个标准,定义了一个字符集以及这个字符集对应的一系列编码方案,即 Unicode 字符集和 UTF-8、UTF-16、UTF-32 等等编码。

通常我们说的 Unicode 仅仅指的是 Unicode 字符集,这个字符集的目的是收录全世界的所有字符,为每一个字符分配一个唯一的数字编号,这个数字编号用 Unicode 定义的术语来说就是 code point (译作码点或码位)。

Unicode 的码点记作 U+[XX]XXXX,X 表示16 进制数。码点的范围是 U+0000~U+10 FFFF。
类似于 GB 2312 按区划分字符集,Unicode 按平面(plane)划分字符集,将字符集划分为 17 个平面(plane 0 到 plane 16),每个平面包含 65536 个码点,码点范围是 U+0000~U+FFFF。其中,第一个平面,即 plane 0,又叫做 BMP(Basic Multilingual Plane,基本多语言平面),包含了世界上日常使用的绝大部分字符。

Unicode 字符集参考:
https://unicode-table.com/cn/

UTF-8、UTF-16、UTF-32

UTF 是 Unicode Transformation Format 的缩写,规定了 Unicode 码点转换为计算机内码的规则。

UTF-32 最简单粗暴,直接使用码点作为四字节内码。比较浪费空间,不是很常用。

考虑到平时使用的大多数字符都在 BMP(U+0000~U+FFFF)里,只需要使用两个字节编码即可。万一用到 BMP 以外的字符,再使用四个字节编码。这就是UTF-16。Java8 内部就是使用 UTF-16 编码字符串。

由于 UTF-16 和 UTF-32 都是一次读取 2 字节或 4字节,这样一是不兼容 ASCII 码,二是在有大小端之分的机器间传输时,需要考虑字节序的问题,否则会造成乱码。

UTF-8 不同于 UTF-16、UTF-32,能够兼容 ASCII 码,是一种变长字节的编码方式,每次读取一个字节,所以无需考虑字节序。

Unicode 标准提出使用 BOM(Byte Order Mark)来标识字节序。做法是在文件开头加上 “ZERO WIDTH NO-BREAK SPACE” 这个字符,这是一个零宽度不换行空格,是一个不可见字符,对应 Unicode 码点为 U+FEFF。
按理说 UTF-8 是不需要 BOM 的,但是为了在解码时明确区分 UTF-8 和其它编码,也可以加上 BOM,Unicode 标准也允许这么做。微软的很多软件就会默认在 UTF-8 编码的文件中加上 BOM,不过有时候,这种做法会造成很多不必要的麻烦。

下表是各种 UTF 的 BOM:

UTF 编码 BOM(十六进制)
UTF-8 without BOM
UTF-8 with BOM EF BB BF
UTF-16LE FF FE
UTF-16BE FE FF
UTF-32LE FF FE 00 00
UTF-32BE 00 00 FE FF

UTF-8 编码规则

UTF-8 使用 1 至 4 个字节为每个字符编码:

  • Unicode 码点在 U+0000~U+007F 之间的 ASCII 码字符,直接使用 1 字节编码;
  • Unicode 码点在 U+0080~U+07FF 之间的字符,包括带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文等,使用 2 字节编码;
  • 其他语言的常用字符(包括中日韩文字、东南亚文字、中东文字等),使用 3 字节编码;
  • 其他极少使用的语言字符,使用 4 字节编码;

UTF-8 编码规则表:

Unicode 码点 bit 数 UTF-8(xxx 就是二进制的 Unicode 码点) byte 数
U+0000~U+007F 7 0xxx xxxx 1
U+0080~U+07FF 11 110x xxxx 10xx xxxx 2
U+0800~U+FFFF 16 1110 xxxx 10xx xxxx 10xx xxxx 3
U+1 0000~U+1F FFFF 21 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx 4

以汉字“码”举例,说明 UTF-8 的编码过程:

Unicode 码点 U+7801
对应二进制 0111 1000 0000 0001
分为三部分 0111、1000 00、00 0001
UTF-8 模板 1110 xxxx 10xx xxxx 10xx xxxx
填充对应位置 1110 0111 1010 0000 1000 0001
编码结果 0xE7A081

UTF-16 编码规则

UTF-16 使用 2 或 4 个字节为每个字符编码:

  • Unicode 码点在 U+0000~U+FFFF 之间的字符,直接使用 2 字节编码。
  • Unicode 码点在 U+10000~U+10FFFF 之间的字符,使用 4 字节编码。具体来说就是:将码点减去 0x10000,然后把结果对应的二进制数分为两部分,高位部分用一个介于 0xD800~0xDBFF 之间的双字节存储,低位部分用一个介于 0xDC00~0xDFFF 之间的双字节存储。

UTF-16 编码规则表:

Unicode 码点范围 bit 数 UTF-16(二进制) byte 数
U+0000~U+FFFF 16 xxxx xxxx xxxx xxxx 2
U+1 0000~U+10 FFFF 20 1101 10xx xxxx xxxx 1101 11xx xxxx xxxx 4

2 字节编码直接使用码点没什么好说的,下面举个例子说明一下 4 字节编码:
汉字<>
Unicode码点 U+20000
减去0x10000 0x10000
对应二进制 0001 0000 0000 0000 0000
分成两部分 0001 0000 00、00 0000 0000
UTF-16 模板 1101 10xx xxxx xxxx 1101 11xx xxxx xxxx
填充对应位置 1101 1000 0100 0000 1101 1100 0000 0000
编码结果 0xD840DC00

位于 U+D800~U+DFFF 之间的 Unicode 码点是特别为四字节的 UTF-16 编码预留的,在这个范围内的码点没有指定任何字符。

记事本输入“联通”保存,再次打开时乱码的问题:

简单来讲就是,windows 自带的记事本,输入”联通“后,默认按照 ANSI 编码保存,ANSI 编码在中国指的就是 GBK 编码。而打开时,记事本发现文件也符合 UTF-8 的编码格式,便优先使用 UTF-8 解码,从而导致乱码。

具体的,“联通”的 GBK 编码为 0xC1AA 0xCDA8,都符合 UTF-8 的双字节编码格式(110x xxxx 10xx xxxx)。

参考:
https://baike.baidu.com/item/%E4%BF%A1%E6%81%AF%E4%BA%A4%E6%8D%A2%E7%94%A8%E6%B1%89%E5%AD%97%E7%BC%96%E7%A0%81%E5%AD%97%E7%AC%A6%E9%9B%86/8074272?fromtitle=GB2312&fromid=483170&fr=aladdin
https://baike.baidu.com/item/Unicode/750500?fr=aladdin#4_1
https://baike.baidu.com/item/UTF-8/481798?fr=aladdin
https://www.zhihu.com/question/23374078/answer/24385963
https://zhuanlan.zhihu.com/p/26261762?utm_source=qq&utm_medium=social&utm_oi=934366346112811008
https://blog.csdn.net/guxiaonuan/article/details/78678043#commentBox

你可能感兴趣的:(字符编码(GB 2312、GBK、UTF-8、UTF-16))