Golang读写CSV文件_encoding/csv标准库使用详解

2次阅读

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

Golang读写CSV文件_encoding/csv标准库使用详解

csv.Reader 读取时卡住或提前结束,大概率是没处理换行符差异

windowsrnmacos/linuxn 在 CSV 中都合法,但 csv.Reader 默认按 n 切分行;如果文件末尾缺换行、或混用 r(比如 excel 旧版本导出),Read() 可能返回 io.EOF 前就停在最后一行,甚至阻塞在 ReadAll()

  • 读取前先用 bytes.ReplaceAll 统一换行:把 rnr 都转成 n,再丢给 csv.NewReader
  • 别直接传 *os.Filecsv.NewReader,先套一层 bufio.NewReader,它对换行更鲁棒
  • 检查 err == io.EOF 后,再确认 records 是否为空——空切片不等于没数据,可能是解析失败被跳过

写 CSV 时中文乱码,不是编码问题而是没写 BOM

goencoding/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 检查字符串,但 x00x01 这类二进制零字节或控制字符虽属有效 UTF-8 编码,却常被 CSV 解析器拒收。错误信息里“illegal utf8 sequence”容易误导人以为是编码错,实际是内容非法。

  • 预处理字段:用 strings.map 清掉 x00-x08x0B-x0Cx0E-x1F 这些不可见控制符
  • 别依赖 fmt.Sprintf 拼接字段——如果源数据来自 C 语言接口或二进制解析,可能带隐藏 x00
  • 写入前用 utf8.ValidString(s) + strings.ContainsAny(s, "x00x01x02") 双重校验,比等报错更早发现问题

大文件用 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 源头。

text=ZqhQzanResources