ruby - 如何强制 Ruby 的 CSV 输出中的一个字段用双引号引起来?

标签 ruby csv

我正在使用 Ruby 的内置 CSV 生成一些 CSV 输出。一切正常,但客户希望输出中的名称字段包含双引号,以便输出看起来像输入文件。例如,输入看起来像这样:

1,1.1.1.1,"Firstname Lastname",more,fields
2,2.2.2.2,"Firstname Lastname, Jr.",more,fields

CSV 的正确输出如下所示:

1,1.1.1.1,Firstname Lastname,more,fields
2,2.2.2.2,"Firstname Lastname, Jr.",more,fields

我知道 CSV 的做法是正确的,因为它没有双引号第三个字段,只是因为它嵌入了空格,并且在该字段包含嵌入的逗号时用双引号引起来。为了帮助客户感到温暖和模糊,我想做的是告诉 CSV 始终双引号第三个字段。

我尝试在我的 to_a 方法中将字段用双引号引起来,这会创建一个 "Firstname Lastname" 字段并传递给 CSV,但 CSV mock 我的弱小-人类尝试并输出 """Firstname Lastname"""。这是正确的做法,因为它转义了双引号,所以没有用。

然后我尝试在 open 方法中设置 CSV 的 :force_quotes => true ,它按预期输出双引号包裹所有字段,但客户不喜欢那,这也是我所期望的。所以,这也没有用。

我查看了 Table 和 Row 文档,似乎没有任何内容可以让我访问“生成字符串字段”方法,或者设置“for field n always use quoting”标志的方法。

我正要深入研究源代码,看看是否有一些 super secret 的调整,或者是否有办法对 CSV 进行猴子修补并根据我的意愿对其进行修改,但想知道是否有人有一些特殊知识或有过以前遇到过这个。

而且,是的,我知道我可以推出自己的 CSV 输出,但我不想重新发明经过良好测试的轮子。而且,我也知道 FasterCSV;它现在是我正在使用的 Ruby 1.9.2 的一部分,因此明确使用 FasterCSV 并没有给我带来什么特别之处。另外,我没有使用 Rails 并且无意在 Rails 中重写它,所以除非您有使用 Rails 的一小部分实现它的可爱方法,否则请不要打扰。我会否决使用这些方法中的任何一种的任何建议,只是因为你懒得读到这里。

最佳答案

好吧,有一种方法可以做到这一点,但它并不像我希望 CSV 代码允许的那样干净。

我必须继承 CSV,然后覆盖 CSV::Row.<<=方法并添加另一个方法 forced_quote_fields=为了能够定义我想要强制引用的字段,再加上从其他方法中提取两个 lambda。至少它适用于我想要的:

require 'csv'

class MyCSV < CSV
    def <<(row)
      # make sure headers have been assigned
      if header_row? and [Array, String].include? @use_headers.class
        parse_headers  # won't read data for Array or String
        self << @headers if @write_headers
      end

      # handle CSV::Row objects and Hashes
      row = case row
        when self.class::Row then row.fields
        when Hash            then @headers.map { |header| row[header] }
        else                      row
      end

      @headers = row if header_row?
      @lineno  += 1

      @do_quote ||= lambda do |field|
        field         = String(field)
        encoded_quote = @quote_char.encode(field.encoding)
        encoded_quote                                +
        field.gsub(encoded_quote, encoded_quote * 2) +
        encoded_quote
      end

      @quotable_chars      ||= encode_str("\r\n", @col_sep, @quote_char)
      @forced_quote_fields ||= []

      @my_quote_lambda ||= lambda do |field, index|
        if field.nil?  # represent +nil+ fields as empty unquoted fields
          ""
        else
          field = String(field)  # Stringify fields
          # represent empty fields as empty quoted fields
          if (
            field.empty?                          or
            field.count(@quotable_chars).nonzero? or
            @forced_quote_fields.include?(index)
          )
            @do_quote.call(field)
          else
            field  # unquoted field
          end
        end
      end

      output = row.map.with_index(&@my_quote_lambda).join(@col_sep) + @row_sep  # quote and separate
      if (
        @io.is_a?(StringIO)             and
        output.encoding != raw_encoding and
        (compatible_encoding = Encoding.compatible?(@io.string, output))
      )
        @io = StringIO.new(@io.string.force_encoding(compatible_encoding))
        @io.seek(0, IO::SEEK_END)
      end
      @io << output

      self  # for chaining
    end
    alias_method :add_row, :<<
    alias_method :puts,    :<<

    def forced_quote_fields=(indexes=[])
      @forced_quote_fields = indexes
    end
end

这就是代码。调用它:

data = [ 
  %w[1 2 3], 
  [ 2, 'two too',  3 ], 
  [ 3, 'two, too', 3 ] 
]

quote_fields = [1]

puts "Ruby version:   #{ RUBY_VERSION }"
puts "Quoting fields: #{ quote_fields.join(', ') }", "\n"

csv = MyCSV.generate do |_csv|
  _csv.forced_quote_fields = quote_fields
  data.each do |d| 
    _csv << d
  end
end

puts csv

结果:

# >> Ruby version:   1.9.2
# >> Quoting fields: 1
# >> 
# >> 1,"2",3
# >> 2,"two too",3
# >> 3,"two, too",3

关于ruby - 如何强制 Ruby 的 CSV 输出中的一个字段用双引号引起来?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4854900/

相关文章:

ruby-on-rails - Rails_Admin - 如何更改 Post View 中文本字段的大小

ruby-on-rails - 如何知道模型是否在 Ruby 中设置了关系?

csv - 将结构转换为 CSV 字符串

Java-将大量数据写入csv

java - 如何读取文件夹中的任何 CSV 文件并将其合并到一个 CSV 文件中

c# - C# 中的 JSON 字符串到 CSV 和 CSV 到 JSON 的转换

ruby-on-rails - 那是一个变量,一个符号,一个方法,为什么这个有冒号而那个没有?

ruby-on-rails - Ruby、HTTParty、SSL 错误

ruby-on-rails - 基本 Rails 问题 : manually inserting a row into a database table

python - 读取制表符分隔的文件,第一列作为键,其余列作为值