Go原生不支持GBK,直接os.ReadFile+string()必乱码或panic;正确做法是用golang.org/x/text/encoding/gbk.NewDecoder().Bytes()解码为合法UTF-8字节切片,大文件须用transform.NewReader流式处理避免OOM,解码器非线程安全需用sync.Pool缓存复用。

Go 原生不支持 GBK,直接 os.ReadFile + string() 必然乱码或 panic;所谓“高性能转码”,核心不是算法快慢,而是避免两份内存驻留、绕过全量加载、正确复用解码器——否则哪怕文件只有 50MB,也容易 OOM 或静默丢字。
GBK → UTF-8 解码时 panic: illegal byte sequence 怎么办
这不是文件损坏,是 Go 运行时在 fmt.Println、json.Marshal 等操作中校验字符串 UTF-8 合法性时触发的 panic。根源在于你把 GBK 字节当 UTF-8 字符串用了。
- 错误做法:
data, _ := os.ReadFile("log.txt"); fmt.Println(string(data))—— 这只是强制解释字节,不是解码 - 正确路径:先用
gbk.NewDecoder().Bytes()或simplifiedchinese.GB18030.NewDecoder().Bytes()把原始[]byte转成合法 UTF-8 字节切片,再string() - 优先用
golang.org/x/text/encoding/gbk(非 deprecated 的simplifiedchinese.GBK),它对扩展区汉字(如“镕”“煊”)支持更完整 - 错误类型是
encoding.InvalidUnreadableError,不是 panic,必须显式检查err != nil,不能忽略
大文件(>100MB)转码内存爆炸怎么避免
一次性 os.ReadFile + .Bytes() 会同时持有原始 GBK 字节和转码后 UTF-8 字节两份数据,内存峰值 ≈ 文件大小 × 2。
- 用
transform.NewReader包裹*os.File,实现边读边解码:reader := transform.NewReader(f, gbk.NewDecoder()) - 别在已有
bufio.Reader上再套transform.NewReader—— 缓冲区错位会导致双字节字符被截断,解码失败 - 写入目标文件时,也用
transform.NewWriter(dst, gbk.NewEncoder()),然后io.Copy(dstWriter, srcReader),全程流式,内存占用恒定 -
transform.NewReader返回的io.Reader不支持Seek(),需要随机访问就得重开文件或分块处理
UTF-8 → GBK 编码写入文件中文变乱码或空字符串
常见错误是调用 file.WriteString(s) 或 fmt.Fprint(file, s) —— 这些方法总是按 UTF-8 写入字节,而目标编辑器(如 Windows 记事本)按 GBK 解释,必然乱码。
立即学习“go语言免费学习笔记(深入)”;
- 正确做法:构造
transform.NewWriter(file, gbk.NewEncoder()),然后调用w.Write([]byte(s))或fmt.Fprint(w, s)(注意是往 wrapper 写,不是原 file) -
gbk.NewEncoder().String(s)返回的是 GBK 编码的string,但 Go 中 string 仍是 UTF-8 视图,实际内容是 GBK 字节;若需[]byte,得用gbk.NewEncoder().Bytes([]byte(s)) - 遇到无 GBK 码位的字符(如 emoji、生僻字),默认返回
encoding.ErrUnsupported;如需容错,用transform.Chain(gbk.NewEncoder(), transform.RemoveInvalidUTF8),但生产环境慎用——丢字比报错更难排查 - BOM 处理:GBK 没有标准 BOM;若源文件开头有 UTF-8 BOM(
0xEF 0xBB 0xBF),必须提前跳过,否则解码器会报错
真正容易被忽略的点是解码器实例的复用:gbk.NewDecoder() 创建的实例**不是线程安全的**,也不能跨 goroutine 复用;高频场景下要用 sync.Pool 缓存,而不是全局单例或每次都 new —— 否则并发时可能解码错乱或 panic。



















