【问题标题】:Ruby CSV BOM|UTF-8 encoding for StringIORuby CSV BOM|StringIO 的 UTF-8 编码
【发布时间】:2020-01-25 20:52:47
【问题描述】:

Ruby 2.6.3。

我一直在尝试使用bom|utf-8 编码将StringIO 对象解析为CSV 实例,以便剥离BOM 字符(不需要的)并将内容编码为UTF-8:

require 'csv'

CSV_READ_OPTIONS = { headers: true, encoding: 'bom|utf-8' }.freeze

content = StringIO.new("\xEF\xBB\xBFid\n123")
first_row = CSV.parse(content, CSV_READ_OPTIONS).first

first_row.headers.first.include?("\xEF\xBB\xBF")     # This returns true

显然bom|utf-8 编码不适用于StringIO 对象,但我发现它确实适用于文件,例如:

require 'csv'

CSV_READ_OPTIONS = { headers: true, encoding: 'bom|utf-8' }.freeze

# File content is: "\xEF\xBB\xBFid\n12"
first_row = CSV.read('bom_content.csv', CSV_READ_OPTIONS).first

first_row.headers.first.include?("\xEF\xBB\xBF")     # This returns false

考虑到我需要直接使用StringIO,为什么CSV 会忽略bom|utf-8 编码?有什么方法可以从 StringIO 实例中删除 BOM 字符?

谢谢!

【问题讨论】:

  • 在创建 StringIO 实例或基于没有 BOM 的 UTF-8 字符串创建另一个实例之前不能删除 BOM 吗?所有已发布的 StringIO 版本都不支持 BOM 处理。
  • 问题是(从 Ruby 2.4 开始)BOM 是 files 的属性,而不是编码。如果您已经有一个编码字符串,则没有 BOM 之类的东西,因为字符已经根据 BOM 正确读取,现在不需要它了。由于 StringIO 是由字符串支持的——不是文件——它也不理解 BOM。

标签: ruby csv utf-8 byte-order-mark ruby-csv


【解决方案1】:

Ruby 不喜欢 BOM。它只在读取文件时处理它们,从不在其他任何地方处理它们,即使这样它也只读取它们以便摆脱它们。如果你想要你的字符串的 BOM,或者写文件的时候想要一个 BOM,你必须手动处理它。

可能有一些宝石可以做到这一点,尽管自己很容易做到

if string[0...3] == "\xef\xbb\xbf"
  string = string[3..-1].force_encoding('UTF-8')
elsif string[0...2] == "\xff\xfe"
  string = string[2..-1].force_encoding('UTF-16LE')
# etc

【讨论】:

    【解决方案2】:

    我发现在 StringIO string 上强制编码为 utf8 并删除 BOM 以生成新的 StringIO 有效:

    require 'csv'
    CSV_READ_OPTIONS = { headers: true}.freeze
    content = StringIO.new("\xEF\xBB\xBFid\n123")
    csv_file = StringIO.new(content.string.force_encoding('utf-8').sub("\xEF\xBB\xBF", ''))
    first_row = CSV.parse(csv_file, CSV_READ_OPTIONS).first
    
    first_row.headers.first.include?("\xEF\xBB\xBF") # => false  
    
    

    不再需要encoding 选项。这可能不是记忆方面的最佳选择,但它确实有效。

    【讨论】:

      【解决方案3】:

      Ruby 2.7 将set_encoding_by_bom 方法添加到IO。该方法使用字节序标记并设置编码。

      require 'csv'
      require 'stringio'
      
      CSV_READ_OPTIONS = { headers: true }.freeze
      
      content = StringIO.new("\xEF\xBB\xBFid\n123")
      content.set_encoding_by_bom
      
      first_row = CSV.parse(content, CSV_READ_OPTIONS).first
      first_row.headers.first.include?("\xEF\xBB\xBF")
      #=> false
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-04
        • 2015-07-29
        • 1970-01-01
        • 2016-05-04
        • 2017-05-08
        相关资源
        最近更新 更多