Go读取非UTF-8文件必须显式解码,优先检测BOM(UTF-8/16/32),再fallback用charset.DetermineEncoding采样推断,最后封装为ReadTextFile函数统一处理。

Go 默认只按 UTF-8 解析字符串,读取带 BOM 或非 UTF-8 编码(如 GBK、Windows-1252、Shift-JIS)的文件时,不处理编码会直接乱码。必须在读取过程中显式解码,不能靠“自动猜”或忽略 BOM。
检测并处理常见 BOM 头
BOM 是最可靠的编码线索,比内容采样更早、更准。Windows-1252、GBK、UTF-16 等编码本身不带 BOM,但 UTF-8/UTF-16/UTF-32 的 BOM 出现频率高,应优先检查。
- 读取前 4 字节即可覆盖所有常见 BOM:UTF-8(
EF BB BF)、UTF-16 LE(FF FE)、UTF-16 BE(FE FF)、UTF-32 LE(FF FE 00 00)、UTF-32 BE(00 00 FE FF) - 匹配到 BOM 后,跳过对应字节数再把剩余内容交给解码器;没匹配到才走后续编码检测逻辑
-
golang.org/x/text/encoding/unicode提供了UTF16和UTF8的NewDecoder(),可直接用于 BOM 分支
用 charset.DetermineEncoding 做 fallback 检测
无 BOM 时,靠内容采样推断编码是次优但必要的手段。别自己写启发式规则——用 golang.org/x/net/html/charset 的 DetermineEncoding,它基于 Mozilla 的算法,支持 ISO-8859-*、Windows-125x、GBK、Big5 等常见编码。
- 采样长度建议至少 1024 字节,太短容易误判(比如纯 ASCII 内容会被当成 ASCII 或 UTF-8)
-
DetermineEncoding返回nil表示无法判断,此时应默认回退到unicode.UTF8,而不是 panic - 注意:它不支持所有编码(例如不识别 Shift-JIS),对未覆盖编码需额外 fallback 到指定编码(如已知文件来源是日文系统,可硬设
jp.JIS)
封装成可复用的 ReadTextFile 函数
把 BOM 检测、fallback 检测、解码器注入、逐行读取打包成一个函数,避免每次重复写样板逻辑。
立即学习“go语言免费学习笔记(深入)”;
- 输入是文件路径,输出是
[]string(每行去掉换行符),错误直接返回 - 内部用
io.MultiReader把已读的 BOM 剩余部分和原文件拼接,保证字节流不丢 - 解码后用
bufio.Scanner逐行读,而非ReadString('\n'),避免因换行符差异(\r\nvs\n)导致截断 - 示例关键片段:
source = io.MultiReader(bytes.NewReader(remaining), file)<br>decoder := e.NewDecoder()<br>utf8Reader := transform.NewReader(source, decoder)<br>scanner := bufio.NewScanner(utf8Reader)
真正麻烦的不是“怎么写”,而是“哪些编码根本没官方 encoder”。比如 Windows-1252 在 golang.org/x/text/encoding/charmap 里有,但某些小众东欧编码可能得自己实现 transform.Transformer。遇到这种,优先确认是否真需要——很多所谓“Windows-1252 文件”其实只是含 Latin-1 字符的 UTF-8,被错误声明罢了。


















