UTF-8
UTF-8 | |
---|---|
术语名称 | UTF-8 |
英语名称 | UTF-8 |
别名 | Unicode Transformation Format – 8-bit |
UTF-8 是一个将 Unicode 字符集编码为字节流的字符编码方法。 UTF 即 Unicode Transformation Format , Unicode 传输格式。 UTF-8 的全称为 Unicode Transformation Format – 8-bit ,即 Unicode 传输格式–8位。
这一编码是变长编码,每一个 Unicode 码位会被编码为 1 至 4 个字节。如果按照最初的范围,理论上可以支持到 6 字节的多字节字符,但现代 Unicode 中不存在足够大的码位,不再需要后续形式。其编解码速度较快,且可以跨国使用,目前应该是最常见最广泛使用的字符编码。
编码规则
UTF-8 是一种前缀码,在编码后的文本中,只会出现三类字节: 0x00-7F
表示其本来的含义以兼容 ASCII , 0x80-BF
这些字节是一个字符的后续字节,而 0xC0-0xFD
则是一个字符的起始字节。通过位运算可以标记为,字节中最高位为 0
的表示这个字符自身,最高位为 10
的表示这是一个后续字节(continuation byte),而有更多 1
开头的位数则意味着这是一个起始字节(first byte),一个起始字节意味着这是一个新字符的开始,组成这一字符的字节数对应于这里 1 的个数,且后续字符均为这一开头。
UTF-8 使用单字节的 0x00-7F
,即二进制 0b0??? ????
,编码原本的范围,也就是 U+0000-007F
。
然后,双字节编码 0xC0-DF.80-BF
,即二进制 0b110? ????.10?? ????
,其中有 11 位可变,因此可用于编码码位在 11 位以下的范围,即 U+0000-U+07FF
。其中由于 U+0000-007F
不以这个方式编码,实际上不会使用首字节为 0xC0-C1
的编码。
然后,三字节编码 0xE0-EF.80-BF.80-BF
,即二进制 0b1110 ????.10?? ????.10?? ????
,其中有 16 位可变,因此可用于编码码位在 16 位以下的范围,即基本多文种平面 BMP 。
然后,四字节编码 0xF0-F7.80-BF.80-BF.80-BF
,即二进制 0b1111 0???.10?? ????.10?? ????.10?? ????
,其中有 21 位可变,因此可用于编码码位在 21 位以下的范围。理论上这可以取到 U+1FFFFF
以内的全部码位, 但是由于现代 Unicode 中最大码位为 U+10FFFF
,这样的序列最大只能取到 0xF4.8F.BF.BF
,再大的序列为非法序列。
理论上,五字节编码 0xF8-FB.80-BF.80-BF.80-BF.80-BF
可编码 26 位以下,即不超过 3FF FFFF
的范围,而六字节编码 0xFC-FD.80-BF.80-BF.80-BF.80-BF.80-BF
可编码到全部 31 位,即一直到 7FFF FFFF
的范围,容纳全部 UCS-4 的数据,但是由于这些范围全部超过 Unicode 的码位空间了,应当视为非法序列。
设计上 0xFE-FF
也是完全不会出现在这个编码中的。
特征
UTF-8 是一种前缀码,且能随时确认其中每个字符的开始位置,无论是发生截断、发生缺失还是进行搜索等都不会因为部分字节出现失败影响到整个字符串的解析。
过长编码
如字节为 0xC0-C1
一旦出现在 UTF-8 二进制串中,就意味着有一个 U+0000-007F
的字符被错误地使用了更长长度的模板编码,这可能造成一些无效或有害数据在按字节的扫描中被通过,因此应当被处理为错误。其他更长的编码模板中也以此类推这一种情况。
例外是在变体中可能允许 0xC0.80
代表 0x00 NUL
以避免空字符在一些传输场景下出现问题。
错误处理
以下数据对于 UTF-8 的字节序列来说一定是错误的:
- 以一个后续字节开头。
- 等待一个后续字节时(在一个字符对应的字节序列结束前)遇到了非后续字节或字符串尾。
- 过长编码。
- 解析后码位超过
U+10FFFF
的字符。 - 字节
0xC0-C1
(一旦出现意味着过长编码)、0xF5-FF
(一旦出现意味着超过现代 Unicode 范围)。
早期解析器可能忽略错误直接解码,现代通常会在此发生错误或截断,以保证解析结果的安全性。目前的推荐做法是将当前字符解码为“�”(U+FFFD
Replacement Character)并跳过当前字节。注意这里一般是跳过一个字节的解析继续考虑下一个字节,而不是按照长度跳过一个字符,以避免第一个字节本身有数据问题导致跳跃长度不是刚好到达下一个字符开始。
以下数据对于 UTF-8 的字节序列来说可能是错误的:
- 解析后是代理对的一半:代理对所在码位是留给 UTF-16 用 BMP 码位表示辅助平面码位的,理论上不是合法的字符码位,因为正常情况下会使用 UTF-8 直接编码对应码位而不是用 UTF-16 将对应码位编码成代理对后重新用 UTF-8 再编码一次。
但是由于一些场景下缺失需要单独编码半个代理对,这个规则通常会被忽略。
BOM
UTF-8 是单字节编码,不需要判断字节序,因此默认情况下不要求也不推荐 UTF-8 编码中带 BOM 字符。在常见情况下,会有一个叫做“UTF-8 with BOM”也就是“带 BOM 字符的 UTF-8” 被单列为不同于 UTF-8 的编码。如果真的携带这一字符, UTF-8 将把 BOM 编码为 0xEF.BB.BF
。
常见字符编码 | |
---|---|
早期编码 | 电报码、 BCD 码 (BCDIC、EBCDIC)、ASCII (ISO 646) |
ISO 8859 | ISO 8859-1 、 ISO 8859-2 、…… |
国标系列 | IBM 代码页 936 / GB 2312 、 微软代码页 936 / GBK 、 微软代码页 54936 / GB 18030 |
Unicode / ISO 10646 | UTF-7 、 UTF-8、 UTF-16 、 UTF-32 |