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 即汉字内码扩展规范,共收录 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 兼容 GBK 和 ASCII,共收录汉字 70244 个,采用一二四字节可变长编码。
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 是 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 使用 1 至 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 使用 2 或 4 个字节为每个字符编码:
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