将 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_methodxyz 这样的通用名称。尤其是未使用的变量。 【参考方案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。您不需要 beginend 关键字来为您的 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 您需要使用ww+ 属性打开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 写入标准输出或文件名的主要内容,如果未能解决你的问题,请参考以下文章

Linux的标准输入和输出

标准输入标准输出标准错误

如何将终端输出的信息重定向写入文件中呢?

在 Node.js 中写入 CSV

如何将wc的标准输出写入文件?

将标准输出从 subprocess.Popen 保存到文件,并将更多内容写入文件