Go解析CSV和Excel需分别处理:csv.Reader需设TrimLeadingSpace、跳空行;excelize推荐替代tealeg/xlsx,应跳过样式公式、校验sheet存在、用ColumnNumberToName防越界,且二者struct标签不兼容,不宜强求统一。

用 encoding/csv 读 CSV 时字段错位或空行不处理
Go 标准库的 csv.Reader 默认把空行当普通记录,且不跳过注释行或 BOM;一旦源文件有 Excel 导出常见的首行空格、尾部逗号、引号嵌套异常,Read() 就会返回 error: bad record 或字段数量对不上。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 初始化
csv.Reader后立即调用r.Comma = ';'(如遇分号分隔)或r.TrimLeadingSpace = true,避免首尾空格干扰字段对齐 - 用
for record, err := r.Read(); err == nil; record, err = r.Read()循环,但必须在循环内加if len(record) == 0 { continue }跳过空行 - 若 CSV 含 Excel 风格双引号转义(如
"a""b"表示a"b),标准库不支持——得换gocsv或手动预处理字符串 - 注意
ReadAll()会一次性加载全部内容到内存,百万行 CSV 容易 OOM;大文件务必用流式Read()
解析 Excel(.xlsx)必须用第三方库,tealeg/xlsx 已停更
Go 没有官方 Excel 支持,tealeg/xlsx 曾是主流,但自 2019 年起不再维护,解析含公式、合并单元格或新 Excel 格式(如 .xlsb)会 panic 或静默丢数据。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 现在推荐
qax-os/excelize: actively maintained,支持读写 xlsx/xlsb/ods,能处理样式、公式、图片(虽公式值需额外调用CalcCellValue) - 打开文件后别直接
f.GetSheetList()就遍历——先确认f.GetSheetName(0)是否存在,否则索引越界报index out of range - 读单元格用
f.GetCellValue("Sheet1", "A1")最安全;若用行列索引f.GetCellValue("Sheet1", "A1")等价于f.GetCellValue("Sheet1", "A1"),但列名计算别手写string(rune('A') + col),容易越界,应改用excelize.ColumnNumberToName(col+1) - Excel 日期是浮点天数(1900-01-01 起),
GetCellValue返回的是字符串格式化后的值;要原始数值,得用f.GetCellFloat("Sheet1", "A1")再转 time.Time
CSV 和 Excel 共用结构体时,标签(tag)写法差异大
想用同一 struct 解析两种格式?csv tag 和 excelize tag 不兼容:encoding/csv 只认 csv:"name",而 excelize 默认按列顺序映射,需显式用 xlsx:"col:1" 或 xlsx:"name" 配合 SetStructTag。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 不要强求单 struct 通吃——CSV 通常按 header 名匹配,Excel 常按固定列位置读,二者语义不同,硬统一反而增加歧义
- 若坚持复用,定义 struct 时同时写两种 tag:
type User struct { Name string `csv:"name" xlsx:"col:1"` },但要注意excelize的col:1是从 1 开始,csv的name是 header 文本,大小写必须完全一致 - Excel 读取时若列顺序不固定,别依赖
col:x,改用xlsx:"name"并确保第一行是 header,再调用f.GetSheetRow("Sheet1", 1)手动找列索引 - struct 字段类型要和实际数据对齐:CSV 数字字段声明为
int却遇到空字符串,UnmarshalCSV会报错;建议全用string接收,后续再strconv.Atoi
性能关键点:小文件无所谓,大 Excel 文件务必关掉样式和公式解析
excelize 默认加载所有样式、条件格式、公式依赖树,一个 10MB 的 Excel(含图表和样式)可能占用 500MB 内存、解析耗时 3 秒以上;而纯数据表其实只需要单元格值。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 打开文件时传入选项:
f, err := excelize.OpenFile("data.xlsx", excelize.Options{SkipFormula: true, SkipStyle: true}) - 如果只读某几列,别用
f.GetRows()全量加载——改用f.GetSheetMap()获取 sheet 名列表,再逐行f.GetRow("Sheet1", i),配合break提前退出 - CSV 流式读取中做字段校验(比如检查邮箱格式)比全读进内存再校验省 60%+ 时间;但别在
Read()循环里反复调用regexp.MustCompile,应提前编译好var emailReg = regexp.MustCompile(`^.+@.+\..+$`) - 并发解析多个文件?注意
excelize的*File实例不是 goroutine-safe 的,每个 goroutine 必须新建实例,不能复用
Excel 解析最麻烦的从来不是读不到数据,而是你没意识到那个“看起来只是个表格”的文件里,藏着 3 层嵌套的条件格式、17 个隐藏工作表,还有被保护但没密码的工作表——它不会报错,只会默默跳过。










