关于ruby:将CSV写入stdout或filename

Write CSV to stdout or filename

我想创建一个方法,如果给定一些CSV输出写入文件名,如果没有给出,则写入stdout。

似乎我需要以不同的方式处理对CSV的调用,具体取决于它是文件还是stdout,但我真的想将输出流z视为我可以写入的内容而不是它是否是 磁盘上的文件或stdout流。

这可能吗?

以下是我的尝试和错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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)


这适用于stdout和文件名。

它使用$stdout.dup来关闭io而不关闭$stdout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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


我使用辅助方法:

1
2
3
4
5
6
7
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(...)使用:

1
2
3
4
5
def some_method(x, y, z = $stdout)
  write_csv(z) do |csv|
    csv << # ...
  end
end


一种解决方案是不使用Pathname对象,因为它不是IO对象。

如果打开文件,则可以像使用stdout对象一样使用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
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