go数据导入导出核心是控流、映射、反馈:csv用csv.newreader读首行建索引,excel用excelize.getsheetrow避空行陷阱,json慎用omitempty,数据库批量写须分事务限行数。

Go 做数据导入导出,核心不是选库,而是**按场景控流、按字段映射、按错误反馈**——CSV 用 csv.NewReader 手动读首行建列索引,Excel 用 excelize.OpenReader 配合 GetSheetRow 避开空行陷阱,JSON 别全信 omitempty,数据库批量写必须分事务、限行数。
为什么 csv.NewReader 总把第一行吃掉?
不是库的问题,是调用姿势错了。很多人在循环前手动调一次 reader.Read() 想跳表头,结果把真实数据吞了。
- 有表头:确认后只调一次
_, _ = reader.Read(),之后进循环 - 无表头:别动,直接
for读,否则整批数据偏移 - 更稳做法:用
records, err := reader.ReadAll()(小文件)或边读边处理(大文件),避免靠“读几次”猜逻辑 - 中文字段含逗号时解析失败?那是 CSV 标准行为:
"姓名","城市","备注"合法,张三,北京,爱喝茶会被拆成 3 列——上游导出要选“字段加引号”,别指望 Go 自动修
excelize 读 Excel 为啥空行/合并单元格崩了?
GetRows() 对空行、合并单元格行为不一致;GetSheetRow() 更可控,它返回的是该行所有单元格原始值(空单元格为 "",不是 nil)。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 读取前先调
f.GetSheetList()确认 sheet 名,别硬写"Sheet1" - 逐行用
f.GetSheetRow(sheet, row),比GetRows()稳定得多 - 单元格值是
interface{}:可能是string、float64或time.Time,必须用f.GetCellValue(sheet, "B2")+f.GetCellType()判断类型再转,不能直接fmt.Sprintf("%v") - 日期显示成数字(如
44926)?那是 Excel 内部浮点存储,得手动转:time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).AddDate(0, 0, int(num)-2)
导出到 MySQL,用 LOAD DATA INFILE 还是拼 INSERT VALUES (),()?
本地开发快,但生产基本不可用:secure_file_priv 限制路径,且文件得在数据库服务器上——你上传的文件在应用层,根本够不着。
- 可靠做法:用
db.Prepare("INSERT INTO t VALUES (?, ?, ?)")+ 循环stmt.Exec(),防 SQL 注入又快 - 别手拼 SQL 字符串,Go 的
database/sql不支持多值占位符,得自己构造(?, ?, ?), (?, ?, ?)模板 - 单次
INSERT最多 500–1000 行:超了会触发ERROR 1153 (0x): Got a packet bigger than 'max_allowed_packet' bytes - 每 500 行
tx.Commit()一次,避免长事务锁表或 OOM
json.Marshal 的 omitempty 为啥漏掉关键零值?
omitempty 只跳 ""、0、false、nil,但业务里“数量为 0”和“未填写数量”语义完全不同。
- 金额、库存、状态码这类字段,去掉
omitempty:Amount int `json:"amount"` - 只对真正可选字段保留:
Remark string `json:"remark,omitempty"` - 嵌套结构体也一样:内部字段是否忽略,取决于它自己的 tag,外层
omitempty不穿透
最常被绕开的点其实是内存控制:大 Excel 文件用 excelize.OpenFile() 会解压全载入内存,10MB 文件可能吃掉 300MB;CSV 导入时别把所有 []string 全 append 进一个 slice——边读边校验、边入库、边记错行,才是实际项目能跑通的写法。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










