「这个文件到底是什么编码」是个比看起来难得多的问题——因为纯文本文件里根本没存这个信息。图片有魔数、压缩包有文件头(参见文件魔数速查),而 .txt / .csv / .log 从第一个字节到最后一个字节全是正文。编码是写入那一刻的约定,约定本身不随文件走。
所以判断编码只有两条路:碰上 BOM 这个例外,或者猜。
BOM:唯一的确定性线索
BOM(Byte Order Mark)是写在文件最开头的编码签名:
| 开头字节 | 编码 |
|---|---|
EF BB BF | UTF-8 |
FF FE | UTF-16 LE |
FE FF | UTF-16 BE |
它的本职工作是给 UTF-16 标字节序——FF FE 和 FE FF 用来区分高低位哪个在前。UTF-8 没有字节序问题,所以 UTF-8 的 BOM 纯粹是一句「我是 UTF-8」的自我声明。
麻烦在于生态对这三个字节的态度完全分裂:
- Windows 系依赖它。记事本、Excel 看到
EF BB BF才确信是 UTF-8,否则按本地 ANSI 代码页(简体中文系统即 GBK)解码。 - Unix 系当它是脏数据。shell 脚本开头有 BOM 会让
#!不再位于文件第一个字节,直接执行失败;cat拼接多个带 BOM 的文件,BOM 会混进正文中间;不少解析器把它当成正文开头一个看不见的字符(U+FEFF),字符串比对、按名取列悄悄失败。
所以「要不要 BOM」没有全局正确答案,取决于文件交给谁——这正是 CSV 问题的核心。
Excel 打开 UTF-8 CSV 乱码的完整逻辑
症状:程序导出的 CSV 明明是标准 UTF-8,Excel 双击打开满屏「娴嬭瘯鏁版嵁」。
原因:Excel 双击打开无 BOM 的 CSV 时不做编码猜测,直接按系统 ANSI 代码页解码。简体中文 Windows 的代码页是 936(GBK),UTF-8 字节按 GBK 双字节切分,就得到上面那种乱码——机制和浣犲ソ型乱码完全同源。
三种解法,按推荐度排:
- 加 UTF-8 BOM。Excel 认到签名就按 UTF-8 读。一次转换永久生效,收件人零操作——发给非技术同事的 CSV 一律用这个。
- Excel 里走「数据 → 从文本/CSV」导入,手动指定 65001: UTF-8。不改文件,但每次打开都要重新点一遍。
- 整个文件转成 GBK。适合对接只认 GBK 的老系统,代价是 GBK 装不下 emoji 和部分生僻字——转换时注意工具的丢字提示。
反方向的坑同样存在:带 BOM 的 CSV 交给数据管道(Hive 建表、自写脚本按行 split、部分库的默认参数)时,表头第一列会带上看不见的 U+FEFF,row['id'] 取不到值且报错信息毫无提示。给 Excel 的加 BOM,给程序的去 BOM,两个版本各存一份最省心。
没有 BOM 时,自动识别靠什么
两层手段,可靠性天差地别。
第一层:UTF-8 结构校验,近乎确定。 UTF-8 的多字节序列有严格语法——首字节 110xxxxx 后面必须跟且只跟一个 10xxxxxx,1110xxxx 后面必须跟两个,以此类推。全文扫描一遍就知道是否合法。一段非平凡长度的文本「碰巧」满足这套语法的概率低到可以忽略,所以试解 UTF-8 成功 ≈ 确认是 UTF-8。这也是「拿不准先按 UTF-8 试」的依据。
第二层:双字节编码之间的统计推断,只能猜。 GBK、Big5、Shift_JIS 的双字节范围大量重叠,同一串字节在三种编码下往往都「合法」,只是解出来的字不同。chardet 这类库的做法是比较哪种解码结果更像真实语言——高频字命中率、字符共现概率——输出的是置信度,不是答案。
猜错的高发场景值得记住:
- 文本太短。几十字节的样本统计不显著,这是自动识别翻车的头号原因——判断短文件时尽量找同来源的长文件一起看。
- 简繁混排、中日混排,频率特征互相污染。
- 内容本身偏离日常语言:人名地名清单、生僻字文档。
所以任何识别结果都要以预览为准:看到成段通顺的文字才算确认,置信度 99% 但预览是怪字,就是猜错了。
GB2312 / GBK / GB18030:一条直线上的三代
经常被当成三种编码,其实是严格向后兼容的三代国标:
| 标准 | 年代 | 规模 | 备注 |
|---|---|---|---|
| GB2312 | 1980 | 6763 字 | 「镕」「堃」等字缺失 |
| GBK | 1995 | 21003 字 | Windows 代码页 936 的实体 |
| GB18030 | 2000 | 全 Unicode | 现行强制标准,四字节变长 |
同一份字节按三个名字解码结果几乎一样——用 GBK 解 GB2312 文件永远正确,反过来可能缺字。所以工具识别报 GB2312 还是 GBK 不必纠结,输出时选 GBK 或 GB18030 总是安全的。
至于新建文件:一律 UTF-8。只有对接明确要求 GBK 的老系统(银行对账单、政务导入模板、老 ERP)时才做转换,这正是文件编码转换的主场——拖入文件自动识别原编码,选目标编码即转,CSV 可勾选加/去 BOM,全程本地处理不上传。已经乱掉的文本则走乱码修复,原理见四类乱码的产生机制与可逆性判断。