go标准库encoding/csv需注意flush()、bom和文件指针:writer必须显式flush()防丢数据;写中文需前置utf-8 bom(\xef\xbb\xbf)兼容windows excel;reader前须seek(0,0)并跳过bom;大文件须流式read()而非readall()。

Go 标准库 encoding/csv 能读写 CSV,但直接调 csv.NewReader 或 csv.NewWriter 就跑,大概率丢数据、乱码、错行——核心问题不在“会不会用”,而在 Flush() 调没调、BOM 加没加、文件指针在不在开头。
csv.Writer 必须显式调 Flush(),否则最后一行大概率丢失
csv.Writer 底层用 bufio.Writer 缓冲,默认 4KB。不调 Flush(),数据就卡在内存里,程序退出时直接丢弃。
- HTTP 导出场景下,没
Flush()会导致浏览器只收到部分响应,甚至超时中断 -
defer w.Flush()不可靠:中间panic时defer可能不执行 -
WriteAll()内部会flush,但只覆盖本次调用的数据;后续再Write()还得自己flush - 检查是否写成功不能只看
Write()返回值,还要在Flush()后查w.Error(),它汇总了整个缓冲期内所有失败
写入中文到 Excel,必须手动加 UTF-8 BOM
Windows 版 Excel 默认用 GBK 解析无 BOM 的 UTF-8 CSV,中文全变乱码。这不是 Go 的 bug,是 Excel 的老习惯。
- 加
BOM只需在写任何数据前,对底层*os.File或bytes.Buffer执行:file.Write([]byte("\xef\xbb\xbf")) - 别用
string拼接或fmt.Fprintln写BOM—— 它们可能触发额外编码转换 - macOS Numbers 和 LibreOffice 通常不依赖
BOM,但加了也无害;不加BOM则 Windows Excel 基本不可用 - 不要尝试把字符串转 GBK 写入:
Go标准库不支持,没必要绕路
csv.NewReader 读不到数据?先重置文件指针并跳过 BOM
常见现象是 reader.Read() 立即返回 io.EOF 或空切片,实际文件明明有内容。
- 打开文件后、创建 reader 前,加一句
file.Seek(0, 0),确保从头开始读 - 检测并跳过 BOM:
bytes.TrimPrefix(buf, []byte("\xef\xbb\xbf")),或更稳妥地用io.MultiReader包一层 - 别用
strings.ReplaceAll“删 BOM”——BOM 只该在开头,乱删可能破坏真实数据中的 UTF-8 多字节序列 - 如果 CSV 有表头,必须显式调一次
reader.Read()获取并丢弃(或保存),csv.Reader不会自动跳过
大文件必须流式读取,禁用 ReadAll()
reader.ReadAll() 看似省事,但它是把整个文件加载进内存再切分。10MB CSV 实际可能占 40MB+ 内存(字符串重复分配 + slice 扩容),超 50MB 基本就触发 GC 压力甚至 OOM。
- ✅ 正确:
r := csv.NewReader(file),然后for { record, err := r.Read() } - ❌ 错误:
data, _ := os.ReadFile(); r := csv.NewReader(bytes.NewReader(data))—— 这已经全进内存了 - 记得复用
record切片,避免每行都新分配;用record := make([]string, 0, 16)预估容量 - 逐行读时容易漏掉最后一行:CSV 末尾没换行符时,
r.Read()成功解析最后一行后才返回io.EOF;要先处理record,再判断err == io.EOF
真正麻烦的不是读写动作本身,而是 CSV 没有强制 schema,同一列在不同行可能出现数字、空字符串、"NULL" 混杂——这部分必须靠业务层定义规则,标准库只管字节到字符串的映射。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











