3. UTF-8 定义 (UTF-8 definition)
UTF-8 由 Unicode 标准 [UNICODE] 定义. 相关描述和公式也可见于 ISO/IEC 10646-1 [ISO.10646] 的附录 D.
编码范围 (Encoding Range)
在 UTF-8 中, U+0000..U+10FFFF 范围内的字符 (即 UTF-16 可访问范围) 使用 1 到 4 个八位组组成的序列进行编码.
单八位组序列 (Single-Octet Sequences)
长度为一的 "序列" 中唯一的八位组将最高位设为 0, 其余 7 位用于编码字符编号.
多八位组序列 (Multi-Octet Sequences)
在由 n 个八位组组成的序列中, n>1:
- 初始八位组的 n 个高位设为 1, 随后的一位设为 0
- 该八位组的剩余位包含待编码字符编号中的若干位
- 后续八位组均将最高位设为 1, 其后一位设为 0, 因而每个八位组留下 6 位用于容纳待编码字符中的位
编码表 (Encoding Table)
下表汇总了这些不同八位组类型的格式. 字母 x 表示可用于编码字符编号位的位.
Char. number range | UTF-8 octet sequence
(hexadecimal) | (binary)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
编码算法 (Encoding Algorithm)
将字符编码为 UTF-8 的过程如下:
步骤 1: 确定八位组数量 (Determine Octet Count)
根据字符编号和上表第一列确定所需的八位组数量. 需要注意的是, 表中的各行互斥, 即给定字符只有一种有效编码方式.
步骤 2: 准备高位 (Prepare High-Order Bits)
按照上表第二列准备各八位组的高位.
步骤 3: 填入字符位 (Fill in Character Bits)
用以二进制表示的字符编号中的位填充标为 x 的位. 首先将字符编号的最低位放入序列最后一个八位组的最低位位置, 然后将字符编号中下一更高位放入该八位组的下一更高位位置, 依此类推. 当最后一个八位组中的 x 位填满后, 转到倒数第二个八位组, 再转到再前一个八位组, 如此继续, 直到所有 x 位均被填满.
保留字符范围 (Reserved Character Ranges)
UTF-8 的定义禁止编码 U+D800 到 U+DFFF 之间的字符编号. 这些编号保留给 UTF-16 编码形式使用 (作为代理对), 并不直接表示字符.
从 UTF-16 转换 (Converting from UTF-16)
从 UTF-16 数据编码为 UTF-8 时, 必须先解码 UTF-16 数据以获得字符编号, 然后按上文所述将这些字符编号编码为 UTF-8.
与 CESU-8 比较 (Comparison with CESU-8)
这与 CESU-8 [CESU-8] 形成对比. CESU-8 是一种类似 UTF-8 的编码, 但并非用于 Internet. CESU-8 的运作方式类似 UTF-8, 但它编码的是 UTF-16 代码值 (16 位数量), 而不是字符编号 (码位). 对于大于 0xFFFF 的字符编号, 这会产生不同结果. 这些字符的 CESU-8 编码不是有效的 UTF-8.
解码算法 (Decoding Algorithm)
解码 UTF-8 字符的过程如下:
步骤 1: 初始化二进制数 (Initialize Binary Number)
初始化一个所有位均为 0 的二进制数. 最多可能需要 21 位.
步骤 2: 确定字符位 (Determine Character Bits)
根据序列中的八位组数量和上表第二列确定哪些位用于编码字符编号, 即标为 x 的位.
步骤 3: 分配位 (Distribute Bits)
将序列中的位分配到该二进制数中, 先从序列最后一个八位组中的低位开始, 然后向左推进, 直到没有剩余的 x 位. 此时该二进制数即等于字符编号.
安全要求 (Security Requirement)
上述解码算法的实现必须防止解码无效序列.
无效序列示例 (Examples of Invalid Sequences)
例如, 朴素实现可能会:
- 将过长 UTF-8 序列 C0 80 解码为字符 U+0000
- 将代理对 ED A1 8C ED BE B4 解码为 U+233B4
解码无效序列可能带来安全后果或导致其他问题. 参见下文安全考虑 (第 10 节).