Go标准库encoding/csv需注意字段校验、BOM处理、流式读取及空值语义:设FieldsPerRecord强制字段数,写入前加UTF-8 BOM,用Read循环替代ReadAll,空字段与"NULL"需业务层区分。

Go 标准库的 encoding/csv 足够应付绝大多数 CSV 场景,但直接用容易在换行、引号、空字段、BOM 处翻车。
读 CSV 时字段数量不匹配?检查 csv.Reader 的 FieldsPerRecord
默认情况下,csv.NewReader 允许每行字段数不同。如果 CSV 是严格表结构(比如数据库导出),字段数不一致往往意味着某行含未转义的换行符或逗号。
- 设
r.FieldsPerRecord = -1(默认):跳过校验,靠业务逻辑兜底 - 设
r.FieldsPerRecord = n:强制每行必须有n个字段,否则返回csv.ErrFieldCount - 真实 CSV 常含跨行字段(如带换行的描述文本),此时必须确保源文件已正确用双引号包裹该字段,且
csv.Reader的LazyQuotes设为true
写 CSV 时中文乱码或 Excel 打不开?优先处理 BOM 和编码
Windows Excel 默认用 GBK 解析无 BOM 的 UTF-8 CSV,导致中文显示为乱码;macOS Numbers 则更依赖 BOM。
- 写入前,在
*csv.Writer对应的*os.File或bytes.Buffer开头写入 UTF-8 BOM: - 不要手动转换字符串编码——Go 字符串原生 UTF-8,BOM 是唯一需要显式加的字节
- 避免用
fmt.Fprintln直接写 CSV 行:它不处理引号、逗号、换行,会破坏格式 - 务必调用
w.Write()写切片,再调用w.Flush(),否则缓冲区内容可能丢失
ReadAll 看似方便,但大文件会 OOM
csv.NewReader(r).ReadAll() 会把整个 CSV 加载进内存,对 10MB+ 文件极易触发内存暴涨甚至 panic。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
立即学习“go语言免费学习笔记(深入)”;
- 替代方案:用
for record, err := r.Read(); err == nil; record, err = r.Read()流式读取 - 注意
r.Read()返回的是[]string,每个元素是解析后的字段(已自动去引号、解转义) - 若需类型转换(如字符串转
int),应在循环内做,并及时处理strconv.Atoi等错误,别堆到后面统一处理
空字段、全空白字段、NULL 字符串怎么区分?
CSV 规范中,,, 是两个空字段,,"", 是一个空字符串字段,,NULL, 是字符串 "NULL" —— 标准库不做语义解释,全交由你判断。
-
record[i] == ""表示该字段为空(对应,,或,"",) - 无法从原始值区分
""和"NULL",除非约定协议(例如后端明确文档写“空值用N表示”) - 若需兼容 SQL 导出的
N表示 NULL,得在读取后手动替换:if s == "\N" { s = "" }
真正麻烦的不是读写动作本身,而是 CSV 没有强制 schema,同一列在不同行可能出现数字、空字符串、NULL 字符串混杂——这部分必须靠业务层定义规则,标准库只管字节到字符串的映射。

















