Unicode 简介
Unicode 是国际标准字符集,它将世界各种语言的每个字符定义一个唯一的编码,以满足跨语言、跨平台的文本信息转换
Unicode 字符集的编码范围是 0x0000 - 0x10FFFF , 可以容纳一百多万个字符, 每个字符都有一个独一无二的编码,也即每个字符都有一个二进制数值和它对应,这里的二进制数值也叫 码点 , 比如:汉字 “中” 的 码点是 0x4E2D, 大写字母 A 的码点是 0x41, 具体字符对应的 Unicode 编码(码位)可以查询
符号库(https://www.fuhaoku.net/)
字节和字符
字节(octet)是一个八位的存储单元,取值范围一定是0~255。而字符(character,或者word)为语言意义上的符号,范围就不一定了。例如在UCS-2中定义的字符范围为0~65535,它的一个字符占用两个字节。
字符集和字符编码
字符集是很多个字符的集合,例如 GB2312 是简体中文的字符集,它收录了六千多个常用的简体汉字及一些符号,数字,拼音等字符
字符编码是 字符集的一种实现方式,把字符集中的字符映射为特定的字节或字节序列,它是一种规则
比如:Unicode 只是字符集,UTF-8、UTF-16、UTF-32 才是真正的字符编码规则
Unicode 字符存储
Unicode 是一个符号集, 它只规定了每个符号的二进制值,但是符号具体如何存储它并没有规定。
多个字节存储有大端和小端两种模式。
小端字节序简写为 LE( Little-Endian ), 表示 低位字节在前,高位字节在后, 高位字节保存在内存的高地址端,而低位字节保存在内存的低地址端
大端字节序简写为 BE( Big-Endian ), 表示 高位字节在前,低位字节在后,高位字节保存在内存的低地址端,低位字节保存在在内存的高地址端
比如字符 0xabcd
大端: ab cd
小端: cd ab
UTF-8 编码
utf-8:是一种变长字符编码,被定义为将码点编码为 1 至 4 个字节,具体取决于码点数值中有效二进制位的数量。
UTF-8 的编码规则:
- 对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的, 所以 UTF-8 能兼容 ASCII 编码,这也是互联网普遍采用 UTF-8 的原因之一
- 对于 n 字节的符号( n > 1),第一个字节的前 n 位都设为 1,第 n + 1 位设为 0,后面字节的前两位一律设为 10 。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码
UTF-8用1~4个字节来表示代码点。表示方式如下:
码位(点代码) | 位序列 | 第一字节 | 第二字节 | 第三字节 | 第四字节 |
---|---|---|---|---|---|
U+0000 … U+007F | 00000000-0xxxxxxx | 0xxxxxxx | |||
U+0080 … U+07FF | 00000xxx-xxyyyyyy | 110xxxxx | 10yyyyyy | ||
U+0800 … U+FFFF | xxxxyyyy-yyzzzzzz | 1110xxxx | 10yyyyyy | 10zzzzzz | |
U+10000…U+1FFFFF | 00000000-000wwwxx- xxxxyyyy-yyzzzzzzz | 11110www | 10xxxxxx | 10yyyyyy | 10zzzzzz |
Unicode编码转换成 UTF-8
“中”:U+4E2D
- U+4E2D 在U+0800 ~ u+FFFF 之间,说明可以转换成3个字节的UTF-8
- U+4E2D 转换成2进制:0100111000101101
- 根据编码规则得到:11100100 10111000 10101101 ==》》E4 B8 AD
- U+4E2D的UTF8是:0xE4 0xB8 0xAD
UTF-8 转换成 UTF-16
对于 Unicode 码小于 0x10000 的字符, 使用 2 个字节存储,并且是直接存储 Unicode 码
根据定义,小于 0x10000的字符,只需将UTF-8的值逆推回去,得到的Uncode码就是UTF-16的编码
“中” UTF-8的编码是:0xE4 0xB8 0xAD
- 将UTF-8 编码转换成2进制,11100100 10111000 10101101
- 根据UTF-8编码规则,第一个字节取后4位,第二个字节取后6位,第三个字节取6位。
- 第一个字节取后4位,11100100 & 00001111 = 0000 0100
- 第二个字节取后6位,10111000 & 00111111 = 0011 1000
- 第三个字节取后6位,10101101 & 00111111 = 0010 1101
- 将获取出来的值重新组成2个字节:0100 111000 101101 =01001110 00101101 =4E 2D(UTF-16BE)
*dest++=(*(src)&0x0f<<4) | (*(src + 1)&0x3c>>2)
*dest++=(*(src+1)&0x03<<6) | (*(src + 2)&0x3f)
UTF-16 编码
UTF-16 也是一种变长字符编码, 这种编码方式比较特殊, 它将字符编码成 2 字节 或者 4 字节
UTF-16 具体的编码规则如下:
- 对于 Unicode 码小于 0x10000 的字符, 使用 2 个字节存储,并且是直接存储 Unicode 码,不用进行编码转换
- 对于 Unicode 码在 0x10000 和 0x10FFFF 之间的字符,使用 4 个字节存储,这 4 个字节分成前后两部分,每个部分各两个字节,其中,前面两个字节的前 6 位二进制固定为 110110,后面两个字节的前 6 位二进制固定为 110111, 前后部分各剩余 10 位二进制表示符号的 Unicode 码 减去 0x10000 的结果
- 大于 0x10FFFF 的 Unicode 码无法用 UTF-16 编码
下表是Unicode编码对应UTF-16编码格式
码位(点代码) | 具体Unicode码(二进制) | UTF-16编码方式(二进制) | 字节 |
---|---|---|---|
0000 0000 - 0000 FFFF | xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx | 2 |
0001 0000 - 0010 FFFF | yy yyyyyyyy xx xxxxxxxx | 110110yy yyyyyyyy 110111xx xxxxxxxx | 4 |
表格中第一列是Unicode编码的范围,第二列是 具体Unicode码的二进制 ( 第二行的第二列表示的是 Unicode 码 减去 0x10000 后的二进制 ) , 第三列是对应UTF-16编码方式,其中白色的二进制 “1” 和 “0” 是固定的前缀, 字母 x 和 y 表示可用编码的二进制位, 第四列表示 编码占用的字节数
Unicode编码小于0x10000:
“中” 字的 Unicode 码是 4E2D, 它小于 0x10000,根据表格可知,它的 UTF-16 编码占两个字节,并且和 Unicode 码相同,所以 “中” 字的 UTF-16 编码为 4E2D
Unicode编码在0x10000 和 0x10FFFF 之间的字符:
Unicode码是U+11660的,
- Unicode码大于0x10000,小于0x10FFFF,按编码规则减去 0x10000,得到值为:0x1660
- 0x1660 二进制码位:00000001011001100000,得到一个20位的二进制值。
- 1101100000000101 、1101111001100000 转换成16进制 D805 、DE60
- UTF-16BE : 0xD8 0x05 0xDE 0x60
UTF-32 编码
UTF-32 是固定长度的编码,始终占用 4 个字节,足以容纳所有的 Unicode 字符,所以直接存储 Unicode 码即可,不需要任何编码转换。
BOM
BOM 是 byte-order mark 的缩写,是 “字节序标记” 的意思, 它常被用来当做标识文件是以 UTF-8、UTF-16 或 UTF-32 编码的标记
在 Unicode 编码中有一个叫做 “零宽度非换行空格” 的字符 ( ZERO WIDTH NO-BREAK SPACE ), 用字符 FEFF 来表示
对于 UTF-16 ,如果接收到以 FEFF 开头的字节流, 就表明是大端字节序,如果接收到 FFFE, 就表明字节流 是小端字节序
UTF-8 没有字节序问题,上述字符只是用来标识它是 UTF-8 文件,而不是用来说明字节顺序的。“零宽度非换行空格” 字符 的 UTF-8 编码是 EF BB BF, 所以如果接收到以 EF BB BF 开头的字节流,就知道这是UTF-8 文件。
下面的表格列出了不同 UTF 格式的固定文件头
UTF编码 | 固定文件头 |
---|---|
UTF-8 | EF BB BF |
UTF-16LE | FF FE |
UTF-16BE | FE FF |
UTF-32LE | FF FE 00 00 |
UTF-32BE | 00 00 FE FF |
根据上面的 固定文件头,下面列出了 “中” (U+4E2D)字在文件中的存储 ( 包含文件头 )
编码 | 固定文件头 |
---|---|
Unicode 编码 | 0X004E2D |
UTF-8 | EF BB BF 4E 2D |
UTF-16BE | FE FF 4E 2D |
UTF-16LE | FF FE 2D 4E |
UTF-32BE | 00 00 FE FF 00 00 4E 2D |
UTF-32LE | FF FE 00 00 2D 4E 00 00 |