常用字符编码总结
相关机构与历史
(1) 国际标准化组织(ISO),他们于1984年创建ISO/IEC JTC1/SC2/WG2工作组,试图制定一份“通用字符集”(Universal Character Set,简称UCS),并最终制定了ISO 10646标准。
(2) 统一码联盟,他们由Xerox、Apple等软件制造商于1988年组成,并且开发了Unicode标准(The Unicode Standard)。
1991年前后,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。于是,它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode 2.0开始,Unicode采用了与ISO 10646-1相同的字库和字码;ISO也承诺,ISO 10646将不会替超出U+10FFFF的UCS-4编码赋值,以使得两者保持一致。两个项目仍都独立存在,并独立地公布各自的标准。不过由于Unicode这一名字比较好记,因而它使用更为广泛。
Unicode编码点分为17个平面(plane),每个平面包含216(即65536)个码位(code point)。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从0016到1016,共计17个平面。
什么是字符集和字符编码
字符集(Charset):符号的集合,每个符合有个唯一数字表示(称之为码位,code point)。但是这个数字在计算机上如何存储,字符集不关心。下面是unicode字符集中的一个字符和其对应的唯一码位。
字符编码(Character Encoding):字符编码和字符集的重要区别是,字符编码约定了字符集的码位在计算机上是如何存储的。计算机是用二进制来表示和传输数据的。人理解的字符要计算机处理,也必须处理成二进制的格式。将字符集的码位和二进制数映射起来的过程可以理解为编码。一个7位的二进制数可以表示2^7=128个码位,也就是意味着最多可以将128个字符的码位表示成二进制数。
注意:一般来说字符集和字符编码名字都是相同的,比如ASCII字符集关联的事ASCII编码,GBK字符集关联GBK字符编码。
常见字符集字符编码
ASCII字符集&字符编码
这个是60年代美国人搞出来给自己用的。码位用127个数字表示。码位的存储上,最高位一直是0,后7位表示128个字符,这个对于英文来说是很足够了。ASCII字符编码中的每个字符占用空间大小为1字节。欧洲的很多其他国家主要也使用英文字母,特定的字符集都是拓展了ASCII,将其最高位利用起来,将编码值范围 (128,255] 利用起来表示本国特有的一些字符。
Unicode字符集
这个字符集非常大,码位的范围是[0,0x10FFFF],最多可以容纳17 × 65,536 = 1,114,112个字符。unicode只是约定了字符集,但是没有约定这些码位如何存储。不同unicode字符编码方式的差异主要体现在存储开销和索引性能上的差异。
UTF-8
查看unicode的码位,可以看到不同的码位存储需要的最小字节数都是不同的。例如000F使用1个字节就可以存储,但是FFFF就需要至少2个字节才可以存储的下。** UTF-8是一个变长字符编码**,它将unicode的码位按照存储使用的字节数进行划分,不同的码位在存储上采用不同的编码方式,对于空间会更加节约一些,存储占用的字节数为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 码 (高位空余则补0)
Unicode编码范围(16进制) | UTF-8编码模板(二进制) |
---|---|
000000 - 00007F | 0xxxxxxx (和ASCII码保持一致) |
000080 - 0007FF | 110xxxxx 10xxxxxx |
000800 - 00FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
01 0000 - 10 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
下面通过一个例子来加深理解UTF-8编码。字符“万”的编码是4e07,在上面表格中属于第三档的范围,也就是说需要用1110xxxx 10xxxxxx 10xxxxxx来表示,4e07表示成二进制位是 0100 1110 0000 0111 ,将这个二进制位替换表格中第三档中编码模板中的x值,则得到**1110 **0100 1011 1000 1000 0111 (十六进制表示为 0xe4B887)
优点
- 存储上节约开销
缺点
- 无法表示完整的unicode字符,大于10 FFFF的字符无法表示
- 不可以按照码位直接编码,涉及套用规则,有一些性能开销
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编码范围(16进制) | UTF-16编码方式(二进制) |
---|---|
0000 0000 - 0000 FFFF | xxxxxxxx xxxxxxxx (2字节) |
0001 0000 - 0010 FFFF | 110110yy yyyyyyyy 110111xx xxxxxxxx (4字节) |
套用编码规则的方式与utf-8相同,这里不赘述了。
优点
- 存储开销上相比UTF-8稍微大一些,但是仍然是尽量节约存储开销了
缺点
- 无法表示完整的unicode字符,大于10 FFFF的字符无法表示
- 不可以按照码位直接编码,涉及套用规则,有一些性能开销
UTF-32
定长的编码方式。始终占用4个字节,可以表示完整的unicode字符。例如按照UTF32BE编码,万这个字符utf-32表示为0x00004e07
UCS-2
在Unicode与ISO 10646合并之前,ISO 10646标准为“通用字符集”(UCS)定义了一种16位的编码形式(即UCS-2),其编码固定占用2个字节(定长2字节编码),它包含65536个编码空间(可以为全世界最常用的63K字符编码,为了兼容Unicode,0xD800-0xDFFF之间的码位未使用)
UCS-4
在Unicode与ISO 10646合并之前,ISO 10646标准为“通用字符集”(UCS)定义了一种31位的编码形式(即UCS-4),其编码固定占用4个字节(定长4字节编码),编码空间为0x00000000~0x7FFFFFFF(可以编码20多亿个字符)。
UCS-4有20多亿个编码空间,但实际使用范围并不超过0x10FFFF,并且为了兼容Unicode标准,ISO也承诺将不会为超出0x10FFFF的UCS-4编码赋值。由此UTF-32编码被提出来了,它的编码值与UCS-4相同,只不过其编码空间被限定在了0~0x10FFFF之间。因此也可以说:UTF-32是UCS-4的一个子集。
多字节编码的字节序
多字节编码存储才会有字节序的问题,UTF-8 最小编码单元是一字节,所以 它是没有字节序的问题。计算机在解析UTF1-6和UTF-32这种多字节存储编码时需要关心其字节序。字节序主要是指编码单元是按照大端还是小端存储的。
- 小端字节序简写为 LE( Little-Endian ):表示 低位字节在前,高位字节在后, 高位字节保存在内存的高地址端,而低位字节保存在内存的低地址端
- 大端字节序简写为 BE( Big-Endian ):表示 高位字节在前,低位字节在后,高位字节保存在内存的低地址端,低位字节保存在在内存的高地址端
下图和表格展示了大小端存储数据的差异,大端展示比较符合人的阅读习惯;计算机读取内存地址是从低地址开始读取的,小端字节序更加符合计算机的处理顺序
物理地址 | 小端模式 | 大端模式 |
---|---|---|
0x80008000 | 0x44 | 0x11 |
0x80008001 | 0x33 | 0x22 |
BOM
BOM 是 byte-order mark 的缩写,是 “字节序标记” 的意思, 它常被用来当做标识文件是以 UTF-8、UTF-16 或 UTF-32 编码的标记。在 Unicode 编码中有一个叫做 “零宽度非换行空格” 的字符 ( ZERO WIDTH NO-BREAK SPACE ), 用字符 FEFF 来表示。对于 UTF-16 ,如果接收到以 FEFF 开头的字节流, 就表明是大端字节序,如果接收到 FFFE, 就表明字节流 是小端字节序.
下面的表格列出了不同 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 |
注意:这个BOM只是个文件头里面的标记,和编码规则没有关系,切勿弄混
数据库字符编码
MySQL
MySQL中也定义了UTF8的字符编码,但是不是真正意义上的UTF8,能表达的变长字符最多3个字节,所以很多占用4个字节的字符在utf8编码的表中是无法存储的。所以后来MySQL引入了utf8mb4字符集,这就相当于完整能力的UTF-8编码了。
参考资料
- 【底层原理】字符编码笔记:ASCII,Unicode 和 UTF-8
- [公众号] Unicode、UTF-8、UTF-16,终于懂了
- 细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4
- [公众号] CPU的大端小端,了解一下