在Ruby中将数组输出为CSV


185

使用Ruby将CSV文件读入数组很容易,但是我找不到关于如何将数组写到CSV文件的任何好的文档。谁能告诉我该怎么做?

如果这很重要,我正在使用Ruby 1.9.2。


3
您的回答很好,但请允许我不要使用CSV。如果您的数据中没有制表符,则制表符分隔的文件将更容易处理,因为它们不需要太多的引用和转义。如果您必须使用CSV,那么当然就是休息时间。
Bill Dueber

8
@ Bill,CSV模块可以巧妙地处理制表符分隔的文件以及实际的csv文件。:col_sep选项可让您将列分隔符指定为“ \ t”,一切顺利。
tamouse

1
这是有关CSV的更多信息 docs.ruby-lang.org/en/2.1.0/CSV.html
veeresh yh

我在此模块中使用.tab文件是我的工作,因为无意间在Excel中打开此文件会另外弄乱编码…
MrVocabulary

Answers:


326

到文件:

require 'csv'
CSV.open("myfile.csv", "w") do |csv|
  csv << ["row", "of", "CSV", "data"]
  csv << ["another", "row"]
  # ...
end

到字符串:

require 'csv'
csv_string = CSV.generate do |csv|
  csv << ["row", "of", "CSV", "data"]
  csv << ["another", "row"]
  # ...
end

这是有关CSV的当前文档:http : //ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html


1
@David这是文件模式。“ w”表示写入文件。如果未指定,则默认为“ rb”(只读二进制模式),尝试添加到csv文件时会出现错误。有关Ruby中有效文件模式的列表,请参见ruby-doc.org/core-1.9.3/IO.html
Dylan Markow

15
知道了 对于将来的用户,如果您希望每次迭代都不会覆盖以前的csv文件,请使用“ ab”选项。
boulder_ruby

1
请参阅以下有关Ruby File IO模式的答案:stackoverflow.com/a/3682374/224707
Nick

37

我已经将这一点降低到仅一行。

rows = [['a1', 'a2', 'a3'],['b1', 'b2', 'b3', 'b4'], ['c1', 'c2', 'c3'], ... ]
csv_str = rows.inject([]) { |csv, row|  csv << CSV.generate_line(row) }.join("")
#=> "a1,a2,a3\nb1,b2,b3\nc1,c2,c3\n" 

完成以上所有操作,然后一行保存到csv。

File.open("ss.csv", "w") {|f| f.write(rows.inject([]) { |csv, row|  csv << CSV.generate_line(row) }.join(""))}

注意:

我认为将活动记录数据库转换为csv就像这样

CSV.open(fn, 'w') do |csv|
  csv << Model.column_names
  Model.where(query).each do |m|
    csv << m.attributes.values
  end
end

嗯@tamouse,这个要点在不阅读csv源代码的情况下有点让我感到困惑,但是通常来说,假设数组中的每个哈希具有相同数量的k / v对并且键始终相同,并且顺序相同(即如果您的数据是结构化的),则应这样做:

rowid = 0
CSV.open(fn, 'w') do |csv|
  hsh_ary.each do |hsh|
    rowid += 1
    if rowid == 1
      csv << hsh.keys# adding header row (column labels)
    else
      csv << hsh.values
    end# of if/else inside hsh
  end# of hsh's (rows)
end# of csv open

如果您的数据不是结构化的,这显然是行不通的


我使用CSV.table提取了一个CSV文件,进行了一些操作,删除了一些列,现在我想将生成的哈希数组再次假脱机为CSV(使用制表符分隔)。如何?gist.github.com/4647196
tamouse

嗯...要点有点不透明,但是给出了一系列散列,所有散列都具有相同的k / v对和相同的键,并且顺序相同...
boulder_ruby

谢谢@boulder_ruby。那可行。数据是一个人口普查表,而要点是不透明的。:)基本上是将原始普查表中的某些列提取到一个子集中。
tamouse

3
您在inject这里滥用,您真的想使用map。另外,您无需将空字符串传递给join,因为这是默认设置。因此,您可以将其进一步缩小:rows.map(&CSV.method(:generate_line).join
iGEL,2016年

1
您的第二个示例过于复杂,因为CSV库非常强大。CSV.generate(headers: hsh.first&.keys) { |csv| hsh.each { |e| csv << e } }生成等效的CSV。
阿玛丹'18

28

如果您有一个数据数组:

rows = [["a1", "a2", "a3"],["b1", "b2", "b3", "b4"], ["c1", "c2", "c3"]]

然后,您可以使用以下代码将此文件写入文件,我认为这要简单得多:

require "csv"
File.write("ss.csv", rows.map(&:to_csv).join)

20

如果有人感兴趣,这里有一些单行代码(以及有关CSV中类型信息丢失的说明):

require 'csv'

rows = [[1,2,3],[4,5]]                    # [[1, 2, 3], [4, 5]]

# To CSV string
csv = rows.map(&:to_csv).join             # "1,2,3\n4,5\n"

# ... and back, as String[][]
rows2 = csv.split("\n").map(&:parse_csv)  # [["1", "2", "3"], ["4", "5"]]

# File I/O:
filename = '/tmp/vsc.csv'

# Save to file -- answer to your question
IO.write(filename, rows.map(&:to_csv).join)

# Read from file
# rows3 = IO.read(filename).split("\n").map(&:parse_csv)
rows3 = CSV.read(filename)

rows3 == rows2   # true
rows3 == rows    # false

注意:CSV会丢失所有类型信息,您可以使用JSON保留基本类型信息,或使用冗长(但更易于人工编辑)的YAML保留所有类型信息-例如,如果您需要日期类型,它将变成CSV和JSON中的字符串。


9

基于@boulder_ruby的答案,这就是我要寻找的东西,假设us_eco其中包含CSV表。

CSV.open('outfile.txt','wb', col_sep: "\t") do |csvfile|
  csvfile << us_eco.first.keys
  us_eco.each do |row|
    csvfile << row.values
  end
end

https://gist.github.com/tamouse/4647196更新了要点


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.