从文字身份到字节
计算机存储和传输的是数值,而读者看到的是文字。字符编码在两者之间建立规则:编码把文本转换为规定的数值序列,解码按相同规则还原。Unicode 中常用 U+ 后接十六进制数字表示码点;码点是代码空间中的编号,不是纸面上字符的图案。
讨论文本时还要区分字符集合、码点分配以及实际编码形式。Unicode 不仅列出字符,还提供字符属性和处理文本所需的规则。说一份文件“使用 Unicode”,通常仍不足以唯一说明其字节如何排列,必须知道采用 UTF-8、UTF-16 等哪一种形式或方案。
UTF-8 的编码与解码
UTF-8 用一至四个字节表示一个 Unicode 标量值,并保留 ASCII 范围原有的单字节表示。Unicode 代理码点不直接作为 UTF-8 字符编码。可变长度意味着字节个数不能直接当作码点个数,也不能随意在任意字节位置截断文本。
以“日”为例,其码点 U+65E5 按 UTF-8 编码得到十六进制字节 E6 97 A5。解码器识别这组三字节的结构,提取承载的位并重建 U+65E5,再交给后续文本处理。相反,英文字母 A 的 U+0041 编成单字节 41。例子中的编号、字节序列和可见文字彼此相关,但不是同一种对象。
参考:[2]
编码不是字体
字形是字符在屏幕或纸面上的视觉表现,字体与排版系统决定其具体外观。Unicode 标识抽象字符,不规定每个字符必须使用的字号、笔画形状或风格。同一字符可以在不同字体中显示不同样貌,编码却不必改变。
因此,缺少字体支持与解码错误是不同故障。前者可能使已正确识别的字符无法正常绘出;后者是在还原字节时就使用了错误规则。换字体不能普遍修复编码解释错误,而更改编码也不能保证字体包含所需字形。
参考:[3]
一个“字”未必一个码点
用户认为的一个文字单位可能由多个码点组成。例如带变音符的字母可以由基本字母和组合附加符构成,也可能存在预组合表示。Unicode FAQ 因而区分字节数、代码单元数、码点数以及更接近用户感知单位的字素簇数;不同计数服务于不同目的。
显示相同或等价的文本也不保证字节逐一相同。比较、搜索和标识符处理可能需要合适的规范化规则,而不只是解码成功。另一方面,解码器还必须识别不合法的 UTF-8 序列,不能把任意字节串都当成合法文字;传输规则、有效性和显示效果需要分别检查。
规范化还分规范等价与兼容等价。NFC把规范等价的组合序列按规则统一,仍保留圈号数字①与普通1之间的兼容区别;NFKC则会把①折叠成1,并移除某些其他格式区别。这不是把UTF-8换成UTF-16,而是改变字符序列本身。Unicode明确提醒不能对任意文本盲用兼容规范化,因为某些区别在数学记号等语境中有意义;搜索匹配与原样保存可以需要不同策略。