BOM 引发的两类故障
第一类是「看不见的污染」:BOM 被当作字符串的第一个字符读入,首行比较、正则首锚定、JSON 解析、CSV 表头匹配都会莫名其妙失败,日志里却看不出任何异常字符。第二类是「看得见的乱码」:带 BOM 的 UTF-8 文件被按单字节或 GBK 编码解释时,EF BB BF 会显示成可见字符,典型表现是内容以「锘」开头或出现  这样的怪串。判断方法很简单:看文件前三字节是否为 EF BB BF。
基本信息
| 故障类型一 | BOM 被当正文:不可见字符污染首行 |
|---|---|
| 故障类型二 | BOM 被当作其他编码解析:显示为锘或  |
| 判定方法 | 读取文件前三字节,比对 EF BB BF |
| 高发文件类型 | Windows 记事本保存的 txt/csv/ini/bat,编辑器默认带 BOM 的源文件 |
| 交付约定 | 需要被解析的文本统一按无 BOM 交付,最省排查成本 |
| 修复成本 | 低:剥离文件头三字节即可,但要先备份 |
典型使用场景
- shell/批处理脚本无法执行:报错信息指向不存在的命令,实际是 shebang 前有 BOM
- CSV 导入失败:表头第一列名带不可见字符,按列名取值全部落空
- JSON/XML 解析报错:内容看起来完全正常,首字符却是一个零宽字符
- 字符串比较与去重异常:两个看起来一模一样的值被判定为不同
- 配置项不生效:ini/yaml 的首个键名被 BOM 污染,匹配不上程序里的键
定位与修复示例
定位与修复示例
head -c 3 bad.txt | xxd # 先确认前三字节是不是 efbbbf
python -c "print(repr(open('bad.txt', encoding='utf-8').read()[:5]))" # 首字符会显示为 '\ufeff'
grep -l $'^\xef\xbb\xbf' *.csv # 批量找出目录里带 BOM 的 CSV
sed -i '1s/^\xEF\xBB\xBF//' bad.csv # 只剥离开头;执行前务必备份
python -c "s=open('bad.csv', encoding='utf-8-sig').read(); print(s.splitlines()[0])" # 读时自动忽略 BOM
# 校验:修复后再次确认首三字节与首行内容
head -c 3 bad.csv | xxd现象与成因对照
| 现象 | 真实原因 | 处理方式 |
|---|---|---|
| 文件开头显示「锘」 | UTF-8 BOM 被按 GBK 解码 | 按 UTF-8 读取,或剥离 BOM 后统一为无 BOM |
| 开头出现  | UTF-8 BOM 被按 Latin-1 解码 | 确认读取编码,避免二次转码 |
| CSV 首列名匹配不上 | 列名首字符是零宽字符 | 入数据前剥离 BOM |
| 首行字符串比较为假 | 字符串首个字符是 U+FEFF | 读入时用感知 BOM 的方式打开 |
| 脚本报找不到命令 | shebang 前有 BOM | 保存为 UTF-8 无 BOM |
排查顺序建议:先看字节(hex 前三字节),再看读取编码,最后才看业务逻辑。绕过字节层直接调试业务代码,往往会在日志里看到「一模一样的两个字符串」比较失败,浪费时间。
批量剥离 BOM 属于危险的批量修改操作:脚本写错正则会误删正文内容,或在 UTF-8 文件被按单字节处理后破坏多字节序列。正确做法是先备份、只处理文件首、处理后抽样验证,并在版本库中审查 diff。
根治手段在源头:编辑器与构建工具统一设置「UTF-8 无 BOM」,代码仓库加 .gitattributes 固定文本文件处理方式,CI 里加一条检查首字节的规则,避免同一个问题反复出现。