6. Byte order mark (BOM) (字节顺序标记)
BOM的定义
UCS字符 U+FEFF "ZERO WIDTH NO-BREAK SPACE" (零宽不换行空格) 也被非正式地称为 "BYTE ORDER MARK" (字节顺序标记,缩写为"BOM")。
BOM的两种用途
用途1: 真正的零宽不换行空格
该字符可以在文本中用作真正的"零宽不换行空格"。
用途2: 字节流签名
BOM这个名称暗示了该字符的第二种可能用途:将U+FEFF字符放在UCS字符流的开头作为"签名"。
BOM作为签名的功能
这种序列化流的接收者可以使用初始字符作为提示:
- 标识流类型: 表明流由UCS字符组成
- 识别编码: 识别所涉及的UCS编码
- 识别字节序: 对于具有多八位字节编码单元的编码,识别八位字节的序列化顺序
UTF-8中的BOM
UTF-8 BOM的字节序列
UTF-8 BOM: EF BB BF
UTF-8具有单八位字节编码单元,因此识别字节序的功能是无用的,BOM将始终显示为八位字节序列 EF BB BF。
⚠️ 使用BOM的注意事项
位置解释规则
出现在流开头以外任何位置的字符U+FEFF:
- 必须 (MUST) 被解释为零宽不换行空格的语义
- 不得 (MUST NOT) 被解释为签名
BOM剥离建议
当被解释为签名时,Unicode标准建议初始的U+FEFF字符可以在处理文本之前被剥离。
何时需要剥离?
在某些情况下,剥离是必要的:
- 例如,连接两个字符串时
- 否则,结果字符串可能在连接点包含意外的"零宽不换行空格"
剥离的影响
剥离可能会影响不同层的外部过程:
- 数字签名
- 字符计数
- 这些过程依赖于流中所有字符的存在
建议做法
因此,建议 (RECOMMENDED):
- 避免在没有充分理由的情况下剥离被解释为签名的初始U+FEFF
- 在适当的时候忽略它而不是剥离它(例如用于显示)
- 仅在真正必要时才剥离它
U+FEFF的歧义性
流的第一位置的U+FEFF 可以 (MAY) 被解释为零宽不换行空格,并不总是签名。
Unicode 3.2的改进
为了减少这种不确定性,Unicode 3.2添加了一个新字符:
U+2060 "WORD JOINER" (词连接符)
- 具有与U+FEFF完全相同的语义和用法
- 除了签名功能
- 强烈建议专门使用它来表达词连接语义
最终,遵循此建议将使得几乎可以确定任何初始的U+FEFF都是签名,而不是预期的"零宽不换行空格"。
协议中BOM的使用限制
与此同时,不确定性仍然存在,并可能影响互联网协议。协议规范可以 (MAY) 限制U+FEFF作为签名的使用,以减少或消除这种不确定性的潜在不良影响。
建议的限制策略
为了在这些限制的优势(减少不确定性)和缺点(失去签名功能)之间取得平衡,区分几种情况是有用的:
情况1: 协议强制使用UTF-8的元素
协议应该 (SHOULD) 禁止对协议强制始终为UTF-8的文本协议元素使用U+FEFF作为签名,因为在这些情况下签名功能完全无用。
情况2: 协议提供字符编码识别机制的元素
当协议为某些文本协议元素提供字符编码识别机制时,如果预期协议的实现将始终能够正确使用这些机制,协议应该 (SHOULD) 也禁止对这些元素使用U+FEFF作为签名。
这种情况发生在:
- 协议元素从创建到(正确标记的)传输期间都在实现的严格控制下
情况3: 协议不提供字符编码识别机制的元素
当以下情况时,协议不应该 (SHOULD NOT) 禁止对这些文本协议元素使用U+FEFF作为签名:
- 协议不提供字符编码识别机制
- 禁令将无法执行
- 预期协议的实现将无法始终正确使用机制
后两种情况可能发生在较大的协议元素上,例如MIME实体,特别是当协议的实现将从以下来源获得此类实体时:
- 文件系统
- 没有有效负载编码识别机制的协议(如FTP)
- 不保证正确识别字符编码的其他协议(如HTTP)
BOM处理规则总结
当协议禁止BOM时
当协议禁止某个协议元素使用U+FEFF作为签名时,该协议元素中的任何初始U+FEFF 必须 (MUST) 被解释为"零宽不换行空格"。
当协议不禁止BOM时
当协议不禁止某个协议元素使用U+FEFF作为签名时,实现应该 (SHOULD) 准备好处理该元素中的签名并做出适当反应:
- 根据需要使用签名来识别字符编码
- 根据情况剥离或忽略签名
UTF-8 BOM使用建议
| 场景 | 建议 | 理由 |
|---|---|---|
| 纯UTF-8文件 | ❌ 不使用BOM | UTF-8没有字节序问题 |
| 协议强制UTF-8 | ❌ 禁止BOM | 签名功能无用 |
| 多编码环境 | ⚠️ 谨慎使用 | 可能导致兼容性问题 |
| Windows记事本 | ℹ️ 自动添加 | 遗留行为,应避免 |
实际示例
Windows记事本的BOM
文件内容 (十六进制):
EF BB BF 48 65 6C 6C 6F
BOM H e l l o
问题: 某些程序可能将BOM显示为乱码
解决方案: 使用更好的编辑器(VS Code, Sublime等)
网页中的BOM问题
<!-- 文件以BOM开始 -->
<!DOCTYPE html>
<html>
...
问题: BOM可能导致:
- HTTP头部已发送后无法设置cookie
- 输出缓冲问题
- 某些浏览器显示问题
解决方案: 移除BOM