Ruby 解析 CSV 文件完全指南:从导入到 ETL 管道

本文全面讲解 Ruby 标准库 CSV 的用法:带表头读取、类型转换、Enumerable 链式处理、大文件流式解析与 CSV 生成,覆盖数据导入和 ETL 场景的实战技巧。

最佳实践
红宝石与应用开发工作台插画

Ruby 标准库自带完整的 CSV 处理类:小文件用 CSV.read + headers: true 拿到行对象数组,大文件用 CSV.foreach 逐行流式处理,配合 converters 自动转型和 Enumerable 方法链即可构建高效的数据导入管道,全程无需第三方 gem。

CSV 库的设计哲学

Ruby 的 CSV 库不是简单的"按逗号 split"——它完整实现了 RFC 4180:带引号的字段、字段内换行、转义引号这些坑都处理好了。永远不要手写 line.split(',') 解析 CSV,那是 bug 发生器。

基础读取

require 'csv'

# 数组模式:每行是数组
CSV.foreach('users.csv') { |row| p row }

# 表头模式:每行是 CSV::Row,按列名取值
CSV.foreach('users.csv', headers: true) do |row|
  puts "#{row['name']} - #{row['email']}"
end

# 一次性全读(仅小文件)
table = CSV.read('users.csv', headers: true)

headers: true 把首行当列名,后续每行变成可按名字索引的 Row 对象,配合 header_converters: :symbol 还能用 row[:name] 符号取值。

类型转换:让数据自己变干净

CSV 里一切都是字符串,手动转型啰嗦且易错。converters 声明式解决:

CSV.foreach('orders.csv',
            headers: true,
            converters: [:numeric, :date]) do |row|
  row['amount']   # => 1299.0(Float)
  row['paid_at']  # => #<Date: 2026-10-08>
end

内置转换器有 :integer、:float、:numeric、:date、:date_time,也可以传 lambda 自定义——比如把 "是/否" 转成布尔值。

Enumerable 链式处理

CSV.read 返回的 Table 支持 Enumerable,转换过滤一气呵成:

table = CSV.read('sales.csv', headers: true, converters: :numeric)
total = table.select { |r| r['region'] == '华东' }
             .sum { |r| r['amount'] }

ETL 场景的经典管道:读入 → 过滤脏行 → 字段映射 → 批量入库。记得在管道入口做表头校验,源文件多一列少一列时及早报错。

大文件流式处理

百万行级文件千万别 CSV.read——内存直接爆炸。CSV.foreach 逐行流式处理,内存恒定:

batch = []
CSV.foreach('huge.csv', headers: true) do |row|
  batch << transform(row)
  if batch.size >= 1000
    User.insert_all(batch)   # 批量入库
    batch = []
  end
end
User.insert_all(batch) if batch.any?

分批 + 批量插入(insert_all)是导入性能的两个数量级开关:单行 insert 导入百万行要几小时,分批 bulk insert 只要几分钟。

生成 CSV

CSV.generate do |csv|
  csv << ['姓名', '邮箱']
  users.each { |u| csv << [u.name, u.email] }
end

# 或直接写文件
CSV.open('export.csv', 'w', write_headers: true,
        headers: %w[name email]) do |csv|
  users.each { |u| csv << [u.name, u.email] }
end

导出给 Excel 用户时加 write_headers 和 BOM("\uFEFF" 开头),中文列名才不会在 Excel 里乱码。

常见问题(FAQ)

Q:解析报 MalformedCSVError 怎么办?

通常是字段内引号未转义或编码不对。先用文本编辑器确认文件编码(转 UTF-8),再检查生成方是否正确转义;实在不行可用 liberal_parsing: true 容错模式,但要在结果里校验字段数。

Q:GBK 编码的老文件怎么读?

指定外部编码:CSV.foreach('old.csv', encoding: 'GBK:UTF-8', headers: true),读取时自动转码成 UTF-8。

Q:分列符不是逗号(如制表符、分号)怎么办?

用 col_sep 选项:CSV.foreach('data.tsv', col_sep: "\t");欧洲格式的分号 CSV 用 col_sep: ';'。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台