csv.reader卡住或提前结束主因是换行符不统一,需用bytes.replaceall将\r\n和\r转为\n;写csv中文乱码因缺bom,应手动写\ufeff;writestring报“illegal utf8”实为含控制字符,需预清理;大文件须流式读取防oom。

csv.Reader 读取时卡住或提前结束,大概率是没处理换行符差异
Windows 的 \r\n、macOS/Linux 的 \n 在 CSV 中都合法,但 csv.Reader 默认按 \n 切分行;如果文件末尾缺换行、或混用 \r(比如 Excel 旧版本导出),Read() 可能返回 io.EOF 前就停在最后一行,甚至阻塞在 ReadAll()。
- 读取前先用
bytes.ReplaceAll统一换行:把\r\n和\r都转成\n,再丢给csv.NewReader - 别直接传
*os.File给csv.NewReader,先套一层bufio.NewReader,它对换行更鲁棒 - 检查
err == io.EOF后,再确认records是否为空——空切片不等于没数据,可能是解析失败被跳过
写 CSV 时中文乱码,不是编码问题而是没写 BOM
Go 的 encoding/csv 默认输出 UTF-8,但 Windows 记事本、Excel 等工具靠 BOM(\uFEFF)识别 UTF-8。没 BOM 就当 ANSI 解码,中文全变问号。
- 写入前手动写 BOM:
w.Write([]byte("\uFEFF")),再调csv.Writer.WriteAll - 别用
os.Create直接打开文件——它默认 truncate,BOM 写进去后文件开头才是\uFEFF;用os.OpenFile(..., os.O_CREATE|os.O_WRONLY|os.O_TRUNC)更稳妥 - 如果目标是 Excel,BOM 必须在第一字节;中间插入或追加写都会失效
csv.Writer.WriteString 失败报 “illegal utf8 sequence”,其实是字段含 \x00 或控制字符
csv.Writer 内部用 utf8.Valid 检查字符串,但 \x00、\x01 这类二进制零字节或控制字符虽属有效 UTF-8 编码,却常被 CSV 解析器拒收。错误信息里“illegal utf8 sequence”容易误导人以为是编码错,实际是内容非法。
- 预处理字段:用
strings.Map清掉\x00-\x08、\x0B-\x0C、\x0E-\x1F这些不可见控制符 - 别依赖
fmt.Sprintf拼接字段——如果源数据来自 C 语言接口或二进制解析,可能带隐藏\x00 - 写入前用
utf8.ValidString(s)+strings.ContainsAny(s, "\x00\x01\x02")双重校验,比等报错更早发现问题
大文件用 ReadAll 会 OOM,必须流式处理 + 控制内存
csv.NewReader.ReadAll() 把整张表加载进内存,10 万行 × 10 列的 CSV 轻松吃掉 200MB+。Golang 不会自动 GC 中间切片,尤其字段含长文本时,内存只增不减。
立即学习“go语言免费学习笔记(深入)”;
- 改用
for record, err := r.Read(); err != io.EOF; record, err = r.Read()循环逐行处理 - 每行处理完立刻
record = nil,避免引用残留;如果 record 要传给 goroutine,用append([]string(nil), record...)拷贝一份 - 配合
runtime.GC()不解决问题,反而拖慢;真要控内存,用sync.Pool复用[]string切片,但注意 Pool 不保证回收时机
CSV 字段里的引号、逗号、换行全得靠双引号包裹和转义,标准库只做基础解析,不负责语义校验——比如数字字段含字母、日期格式错位,得自己加规则。这点容易漏,等数据入库报错才回头查 CSV 源头。










