在 Ruby 中将数组输出到 CSV
Posted
技术标签:
【中文标题】在 Ruby 中将数组输出到 CSV【英文标题】:Output array to CSV in Ruby 【发布时间】:2011-06-16 21:12:40 【问题描述】:使用 Ruby 将 CSV 文件读入数组很容易,但我找不到任何关于如何将数组写入 CSV 文件的好的文档。谁能告诉我怎么做?
如果这很重要,我正在使用 Ruby 1.9.2。
【问题讨论】:
您的答案很好,但我敦促您不要使用 CSV。如果您的数据中没有制表符,则制表符分隔的文件更容易处理,因为它们不涉及太多的引用和转义等。如果您必须使用 CSV,当然,它们就是中断。 @Bill,CSV 模块巧妙地处理制表符分隔的文件以及实际的 csv 文件。 :col_sep 选项允许您将列分隔符指定为“\t”,一切都很好。 这里有更多关于 CSV 的信息docs.ruby-lang.org/en/2.1.0/CSV.html 在这个模块中使用 .tab 文件是我正在做的,因为不小心在 Excel 中打开它会打乱编码…… 【参考方案1】:如果你有一组数据数组:
rows = [["a1", "a2", "a3"],["b1", "b2", "b3", "b4"], ["c1", "c2", "c3"]]
然后你可以把它写到一个文件中,我认为这要简单得多:
require "csv"
File.write("ss.csv", rows.map(&:to_csv).join)
【讨论】:
是的,但是每次都重写文件,而不是最后添加数据 @HumayunNaseer 我不需要将数据附加到 CSV 的情况,但您当然可以使用mode: "a"
选项 File.write
。【参考方案2】:
我只写了一行。
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 文件,进行了一些操作,删除了一些列,现在我想再次将生成的 Hash 数组作为 CSV 输出(真的是制表符分隔) .如何? gist.github.com/4647196 hmm...这个要点有点不透明,但是给定一个哈希数组,所有这些都具有相同数量的 k/v 对和相同的键,以相同的顺序... 谢谢,@boulder_ruby。那可行。数据是一个人口普查表,回顾它的要点是相当不透明的。 :) 它基本上是从原始人口普查表中提取某些列到一个子集中。 你在这里误用了inject
,你真的想用map
。此外,您不需要将空字符串传递给join
,因为这是默认设置。所以你可以进一步缩小它:rows.map(&CSV.method(:generate_line).join
您的第二个示例过于复杂,因为 CSV 库非常强大。 CSV.generate(headers: hsh.first&.keys) |csv| hsh.each |e| csv << e
生成一个等效的 CSV。【参考方案3】:
如果有人感兴趣,这里有一些单行代码(以及关于 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 中的字符串。
【讨论】:
【参考方案4】:基于@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的要点
【讨论】:
【参考方案5】:我自己也在为此苦苦挣扎。这是我的看法:
https://gist.github.com/2639448:
require 'csv'
class CSV
def CSV.unparse array
CSV.generate do |csv|
array.each |i| csv << i
end
end
end
CSV.unparse [ %w(your array), %w(goes here) ]
【讨论】:
顺便说一句,当心 JRuby 上的多维数组。[ %w(your array), %w(goes here) ]
看起来不漂亮。 github.com/pry/pry/issues/568【参考方案6】:
到一个文件:
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
【讨论】:
@David 这是文件模式。 “w”表示写入文件。如果您不指定此选项,它将默认为“rb”(只读二进制模式),并且在尝试添加到 csv 文件时会出现错误。有关 Ruby 中有效文件模式的列表,请参阅 ruby-doc.org/core-1.9.3/IO.html。 明白了。对于未来的用户,如果您希望每次迭代都不会覆盖之前的 csv 文件,请使用“ab”选项。 查看 Ruby 文件 IO 模式的答案:***.com/a/3682374/224707以上是关于在 Ruby 中将数组输出到 CSV的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Android Studio 中将 CSV 文件解析为数组