一、BOM 是什么
BOM(Byte Order Mark)是文件开头的一个特殊标记,UTF-8 的 BOM 是三个字节:
EF BB BF
它原本的用途是标识字节序(大端/小端),但 UTF-8 本身没有字节序问题,所以这个标记在 UTF-8 里纯粹是「我是一个 UTF-8 文件」的提示。问题在于:它不是内容,但很多解析器会把它当内容读进去。
二、它会造成哪些具体故障
| 场景 | 现象 |
|---|---|
| JSON 解析 | JSON.parse 报 Unexpected token,位置在第 1 行第 1 列 |
| PHP / 模板输出 | 页面顶部多出空白,headers already sent 警告 |
| 配置文件 | 第一个键名变成 key,读取时找不到 |
| CSV | 第一个表头多一个不可见字符,字段映射失败 |
| Shell 脚本 | #!/bin/bash 前有 BOM,报 bad interpreter |
| 合并文件 | 多个文件拼接后中间冒出乱码字符 |
这些故障的共同点是报错位置和真实原因完全不匹配,所以特别耗时间。
三、为什么 Excel 反而需要它
Windows 版 Excel 打开 CSV 时靠 BOM 判断「这是 UTF-8」。没有 BOM,它就按本地 ANSI(简体中文是 GBK)解析,中文变乱码。所以:
- 给人用 Excel 打开 → 要 BOM(Python 写文件用
encoding='utf-8-sig')。 - 给程序解析(JSON、配置、脚本)→ 不要 BOM。
这两个需求是冲突的,所以同一份数据不要既当配置文件又当 Excel 表格,导出两份更省事。
四、怎么检测
- 十六进制查看:文件前三个字节是不是
EF BB BF。 - 命令行:
file x.json会显示with BOM;head -c 3 x.json | xxd直接看字节。 - 代码里:读进来后判断
text.charCodeAt(0) === 0xFEFF,是就去掉。
五、怎么去除
# Linux / macOS:去掉开头的 BOM
sed -i '1s/^\xEF\xBB\xBF//' file.txt
# 或用 dos2unix(会顺带处理 BOM)
dos2unix file.txt
# Python:用 utf-8-sig 读,会自动吞掉 BOM
text = open('a.json', encoding='utf-8-sig').read()
# 写出时不带 BOM
open('b.json', 'w', encoding='utf-8').write(text)
// JS:读进来后手动去掉
const clean = str.replace(/^\uFEFF/, '');
注意别用「在编辑器里删掉第一个字符」这种办法——BOM 常常不可见,手动删容易删掉真正的第一个字符。
六、怎么从源头避免
- 编辑器统一设置为「UTF-8 无 BOM」(VS Code 右下角可切换,也可在设置里固定)。
- Windows 记事本另存为时选「UTF-8」而不是「带 BOM 的 UTF-8」(新版记事本默认已无 BOM)。
- 构建流程里加一步检查:所有
.json、.js、.sh文件不得以 BOM 开头。 - 接收外部文件时先清洗一遍再入库。
七、和乱码的区别
BOM 造成的是「多了一个字符」,不是「一串乱码」。如果整个文件中文都是乱码,那是编码不一致(见 中文乱码排查);如果只有第一行第一列出现莫名其妙的错误,先查 BOM。
拿不准某个文件开头是什么字节,可以用 Unicode 转换工具 查看码点,或者直接丢进 JSON 格式化校验——如果它报第 1 行第 1 列错误而内容看起来完全正常,八成就是 BOM。
小结
UTF-8 BOM = 文件开头的 EF BB BF,对程序是垃圾、对 Excel 是信号。给机器读的文件一律无 BOM,给 Excel 看的 CSV 才加;报错位置在第一行第一列且内容正常时,先查 BOM。