
本文介绍使用 Go 语言读取 CSV 文件并根据某列(如姓名)去重的完整实现方案,涵盖内存高效去重逻辑、标准库 encoding/csv 的正确用法、错误处理及性能注意事项。
本文介绍使用 go 语言读取 csv 文件并根据某列(如姓名)去重的完整实现方案,涵盖内存高效去重逻辑、标准库 `encoding/csv` 的正确用法、错误处理及性能注意事项。
在 Go 中对 CSV 数据按指定列(例如 name)去重,核心思路是:利用 map[string]struct{} 记录已出现的键值,结合流式读取实现低内存、高效率的去重处理。相比布尔映射(map[string]bool),struct{} 零内存占用(底层不分配空间),是 Go 社区推荐的轻量级存在性标记方式。
以下是一个完整、可运行的示例,假设 CSV 文件格式为 name,band,year,要求同一 name 仅保留首次出现的记录:
使用 qbo-mileage CLI 及用户凭证,从 Airtable、Outlook 或 Google Calendar 记录生成 QuickBooks Online 里程 CSV 文件。
package main
import (
"encoding/csv"
"fmt"
"os"
)
type Record struct {
Name, Band, Year string
}
func main() {
file, err := os.Open("input.csv")
if err != nil {
panic(fmt.Sprintf("无法打开文件: %v", err))
}
defer file.Close()
reader := csv.NewReader(file)
// 跳过表头(如有)
// header, _ := reader.Read()
seen := make(map[string]struct{}) // 高效去重集合
var uniqueRecords []Record
for {
record, err := reader.Read()
if err == csv.ErrFieldCount {
fmt.Printf("字段数量不匹配,跳过该行\n")
continue
}
if err == io.EOF {
break
}
if err != nil {
panic(fmt.Sprintf("读取 CSV 错误: %v", err))
}
if len(record) < 3 {
fmt.Printf("跳过无效行(字段不足3个): %v\n", record)
continue
}
name := record[0]
if _, exists := seen[name]; !exists {
seen[name] = struct{}{}
uniqueRecords = append(uniqueRecords, Record{
Name: record[0],
Band: record[1],
Year: record[2],
})
}
}
// 输出去重结果
for _, r := range uniqueRecords {
fmt.Printf("%s | %s | %s\n", r.Name, r.Band, r.Year)
}
}⚠️ 关键注意事项:
- 顺序保证:此方法天然保留首次出现的记录,符合“去重留一”需求;
- 内存安全:map[string]struct{} 比 map[string]bool 更省内存(struct{} 占 0 字节),尤其适合大数据量场景;
- 健壮性处理:需显式检查 len(record) >= 3 和 csv.ErrFieldCount,避免 panic;
- 扩展建议:若需按多列组合去重(如 name+band),可拼接键:key := record[0] + "|" + record[1];
- 大文件优化:如 CSV 超过百MB,建议改用 bufio.Reader 包装 os.File 并配合 csv.NewReader 提升 IO 效率,或分批处理+写入临时文件。
总结:Go 的 map + struct{} 是 CSV 列级去重的简洁高效解法。它无需第三方依赖、逻辑清晰、内存友好,非常适合构建数据清洗流水线中的基础去重环节。

















