解析带有标题字段的 CSV 文件作为每行的属性

Posted

技术标签:

【中文标题】解析带有标题字段的 CSV 文件作为每行的属性【英文标题】:Parse CSV file with header fields as attributes for each row 【发布时间】:2011-04-12 15:51:14 【问题描述】:

我想解析一个 CSV 文件,以便将每一行视为一个对象,其中标题行是对象中属性的名称。我可以写这个,但我确定它已经存在了。

这是我的 CSV 输入:

"foo","bar","baz"
1,2,3
"blah",7,"blam"
4,5,6

代码如下所示:

CSV.open('my_file.csv','r') do |csv_obj|
  puts csv_obj.foo   #prints 1 the 1st time, "blah" 2nd time, etc
  puts csv.bar       #prints 2 the first time, 7 the 2nd time, etc
end

使用 Ruby 的 CSV 模块,我相信我只能通过索引访问字段。我认为上面的代码会更具可读性。有什么想法吗?

【问题讨论】:

【参考方案1】:

使用 Ruby 1.9 及更高版本,您可以获得一个可索引的对象:

CSV.foreach('my_file.csv', :headers => true) do |row|
  puts row['foo'] # prints 1 the 1st time, "blah" 2nd time, etc
  puts row['bar'] # prints 2 the first time, 7 the 2nd time, etc
end

它不是点语法,但它比数字索引更好用。

顺便说一句,对于 Ruby 1.8.x,FasterCSV 是您使用上述语法所需要的。

【讨论】:

FasterCSV 被合并到 Ruby 中,我认为它是在 Ruby 1.9+ 中。 如果你真的想要点语法,你可以require ostruct,然后输入row_struct = OpenStruct.new(row.to_h)的步骤,它会响应row_struct.foo【参考方案2】:

这是一个使用 Ruby 1.9 的符号语法示例。在下面的示例中,代码从 Rails db 目录读取名为 data.csv 的 CSV 文件。

:headers => true 将第一行视为标题而不是数据行。 :header_converters => :symbolize 参数然后将标题行中的每个单元格转换为 Ruby 符号。

CSV.foreach("#Rails.root/db/data.csv", :headers => true, :header_converters => :symbol) do |row|
  puts "#row[:foo],#row[:bar],#row[:baz]"
end

在 Ruby 1.8 中:

require 'fastercsv'
CSV.foreach("#Rails.root/db/data.csv", :headers => true, :header_converters => :symbol) do |row|
  puts "#row[:foo],#row[:bar],#row[:baz]"
end

根据 Poul(*** 询问者)提供的 CSV,上面示例代码的输出将是:

1,2,3
blah,7,blam
4,5,6

根据 CSV 文件标头中使用的字符,可能需要输出标头以查看 CSV (FasterCSV) 如何将字符串标头转换为符号。您可以从CSV.foreach 中输出标头数组。

row.headers

【讨论】:

所以我将 CSV 文件加载到一个数组中,循环内只有 allstocks << row。如何读取myrow[:company] 的一个单元格myrow[:ticker] == "ANAD"?只有一条记录,无论如何ticker 是我的关键字段。 Marcos - 如果 CSV 已转换为数组,您可能丢失了哈希(符号)。如果是这种情况,只需按列号引用单元格,例如myrow[0].【参考方案3】:

在 Ruby 2.3 中轻松获取哈希:

CSV.foreach('my_file.csv', headers: true, header_converters: :symbol) do |row|
  puts row.to_h[:foo]
  puts row.to_h[:bar]
end

【讨论】:

Ruby没有内置的符号转换器,所以不需要先加一个:CSV::Converters[:symbol] = ->(v) v.to_sym ?【参考方案4】:

虽然我的讨论很晚,但几个月前我在https://github.com/vicentereig/virgola 开始了“CSV 到对象映射器”。

鉴于您的 CSV 内容,将它们映射到 FooBar 对象数组非常简单:

"foo","bar","baz"
1,2,3
"blah",7,"blam"
4,5,6
require 'virgola'

class FooBar
  include Virgola

  attribute :foo
  attribute :bar
  attribute :baz
end

csv = <<CSV
"foo","bar","baz"
1,2,3
"blah",7,"blam"
4,5,6
CSV

foo_bars = FooBar.parse(csv).all
foo_bars.each  |foo_bar| puts foo_bar.foo, foo_bar.bar, foo_bar.baz 

【讨论】:

刚刚发现使用loaddump 方法(Ruby 1.9/FasterCSV)github.com/JEG2/faster_csv/blob/master/test/tc_serialization.rb 这样的。这实际上是一个非常酷的功能! gist.github.com/3188109【参考方案5】:

因为我经常碰到这个问题:

array_of_hashmaps = CSV.read("path/to/file.csv", headers: true)
puts array_of_hashmaps.first["foo"] # 1

这是非阻塞版本,当您想吞食整个文件时。

【讨论】:

以上是关于解析带有标题字段的 CSV 文件作为每行的属性的主要内容,如果未能解决你的问题,请参考以下文章

在某些情况下使用双引号解析 CSV

使用 gawk 解析 CSV 文件

CSVHelper 使用每个文件上的多个映射解析多个 CSV

CSV 到 CoreData

使用 awk 或 perl 从 CSV 中提取特定列(解析)

Boost tokenizer 无法解析具有双引号字段的 csv 文件