BOM 是什么
BOM(Byte Order Mark)本质是字符 U+FEFF(ZERO WIDTH NO-BREAK SPACE)被放在字节流的最前端:解码器先读它,就能推断接下来是多字节单位的哪一种字节序。UTF-16 下小端写作 FF FE、大端写作 FE FF;UTF-32 下是 FF FE 00 00 或 00 00 FE FF;UTF-8 没有字节序问题,它的 EF BB BF 只是一个「我是 UTF-8」的弱信号。也正因为没有必要性,规范对 UTF-8 BOM 的态度是:允许出现但不应主动添加。
基本信息
| 对应字符 | U+FEFF(零宽不换行空格) |
|---|---|
| UTF-8 BOM | EF BB BF(3 字节) |
| UTF-16 小端 / 大端 | FF FE / FE FF |
| UTF-32 小端 / 大端 | FF FE 00 00 / 00 00 FE FF |
| 主要作用 | 声明字节序(UTF-16/32);标识编码(UTF-8,可选) |
| 位置约束 | 只能在流的最开头出现一次 |
典型使用场景
- 识别 Windows 生成的文件:记事本另存为 UTF-8 的历史版本会带 BOM
- CSV 交付给 Excel:带 BOM 的 UTF-8 能让简体中文 Office 正确识别编码
- 脚本与配置排查:shebang 前多出的 BOM 会让解释器报找不到文件
- 数据清洗:批量检测并剥离文件头的 BOM,保证下游按 UTF-8 正确解析
- 编码判定:开头是 EF BB BF 基本可断定文件为 UTF-8
检测与处理示例
检测与处理示例
head -c 3 file.txt | xxd # 输出 efbbbf 说明带 UTF-8 BOM
file file.txt # 部分系统会直接标注 with BOM
sed -i '1s/^\xEF\xBB\xBF//' file.txt # 仅在文件首剥离 BOM(慎用,先备份)
python -c "print(open('file.txt', encoding='utf-8-sig').readline())" # utf-8-sig 自动吃掉 BOM
python -c "b=open('file.txt','rb').read(3); print(b == b'\xef\xbb\xbf')" # 二进制层判定
# PowerShell 侧判断首三字节,避免把 BOM 当正文
powershell -Command "(Get-Content file.txt -Encoding Byte -TotalCount 3) -join ','"编码与 BOM 字节
| 编码 | BOM 字节 | 是否必要 |
|---|---|---|
| UTF-8 | EF BB BF | 不必要,规范建议不加 |
| UTF-16 小端 | FF FE | 无外部声明时需要 |
| UTF-16 大端 | FE FF | 无外部声明时需要 |
| UTF-32 小端 | FF FE 00 00 | 无外部声明时需要 |
| UTF-32 大端 | 00 00 FE FF | 无外部声明时需要 |
不要全局删除所有 U+FEFF:文件开头的才是 BOM,出现在正文中间的 U+FEFF 是零宽不换行空格,可能是有意插入的排版字符,删掉会改变文本内容。剥离操作应限定在「文件首个字符」。
JSON 规范要求实现不得添加 BOM(可以忽略已存在的 BOM);HTTP 消息头与状态行按字符集定义不允许出现 BOM;Unix shell 脚本带 BOM 会导致内核无法识别 shebang。这些场景都应保存为「UTF-8 无 BOM」。
Windows 生态与 Unix 生态对 BOM 的习惯不同:Windows 工具偏好带 BOM 以便自动识别,Unix 工具链普遍把 BOM 当普通字节。跨平台协作时明确约定「UTF-8 无 BOM」是最省事的做法,代码仓库还应加 .gitattributes 或编辑器配置来固定。