将 CSV 写入标准输出或文件名
Posted
技术标签:
【中文标题】将 CSV 写入标准输出或文件名【英文标题】:Write CSV to stdout or filename 【发布时间】:2017-06-03 22:25:08 【问题描述】:我想创建一个方法,如果给定,则将一些 CSV 输出写入文件名,如果没有给出,则写入标准输出。
似乎我需要根据它是文件还是标准输出来不同地对待我对CSV
的调用,但我真的很想将输出流z
视为我可以写入但没有的东西无论是磁盘上的文件还是标准输出流。
这可能吗?
以下是我的尝试和错误:
require 'csv'
require 'pathname'
require 'csv'
require 'pathname'
def write_to_csv_or_stdout foo, bar, z=nil
z = Pathname.new(z) if z
z ||= $stdout
res = [[foo, bar, "baz"]]
CSV(z) do |csv|
res.each do |row|
csv << row
end
end
end
write_to_csv_or_stdout "foo", "bar"
# foo
# bar
#=> foo,bar,baz
# write_to_csv_or_stdout "foo", "bar", "baz"
# (NoMethodError)
【问题讨论】:
你的目标是什么?请不要使用像some_method
、x
、y
、z
这样的通用名称。尤其是未使用的变量。
【参考方案1】:
这适用于标准输出和文件名。
它使用$stdout.dup
能够在不关闭$stdout
的情况下关闭io
。
require 'csv'
def write_csv(rows, filename = nil)
io = filename ? File.open(filename, 'w+') : $stdout.dup
CSV(io) do |csv|
rows.each do |row|
csv << row
end
end
ensure
io.close
end
rows = [["foo", "bar", "baz"]]
write_csv(rows)
#=> foo,bar,baz
write_csv(rows, 'test.csv')
#=> 'test.csv' written
【讨论】:
感谢您的回复!我签出了another answer。您不需要begin
和 end
关键字来为您的 ensure
工作:)
@mbigras:谢谢。 rubocop 同意你的看法。
您可能希望在关闭时添加一个 nil 检查,以防打开失败:io.close if io
。
@KeithBennett:很有趣。 File.open
返回nil
真的会发生吗?我认为这会引发错误或打开文件。你有没有 io.close
会因 NoMethodError 而失败的例子?
@EricDuminil 这是一个很好的观点。如果代码以下列任何方式更改,则 nil 检查会更安全:1)在文件打开之前添加可能引发错误的内容,2)将条件文件关闭(并设置为 nil)添加到用于其他目的的方法体。也就是说,这段代码很好,感谢您的更正。【参考方案2】:
一种解决方案是不要只使用 Pathname 对象,因为它不是 IO 对象。
如果您打开文件,那么您可以像使用 stdout 对象一样使用它。
def some_method x, y, z=nil
puts x
puts y
z = Pathname.new(z).open if z # <== here you get an IO Object back
z ||= $stdout
res = [["foo", "bar", "baz"]]
CSV(z) do |csv|
res.each do |row|
csv << row
end
end
end
【讨论】:
是否可以告诉CSV(z)
写入一个新文件z
?因为如果z
不存在我会得到No such file or directory @ rb_sysopen
@mbigras 您需要使用w
或w+
属性打开z
。
你认为这种方式更好,因为z
的条件赋值是线性读取而不是分支?
对此我不能客观。 z = x if z; z ||= y
看起来不太对劲。它正在分支。【参考方案3】:
我会使用辅助方法:
def write_csv(io_or_filename, &block)
if io_or_filename.is_a?(IO)
CSV.new(io_or_filename, &block)
else
CSV.open(io_or_filename, 'wb', &block)
end
end
这可以用来代替CSV(...)
:
def some_method(x, y, z = $stdout)
write_csv(z) do |csv|
csv << # ...
end
end
【讨论】:
以上是关于将 CSV 写入标准输出或文件名的主要内容,如果未能解决你的问题,请参考以下文章