【问题标题】:Parsing a CSV file using different encodings and libraries使用不同的编码和库解析 CSV 文件
【发布时间】:2014-01-10 03:48:28
【问题描述】:

尽管有许多关于该主题的 SO 线程,但我在解析 CSV 时遇到了问题。它是从 Adwords 关键字规划师下载的 .csv 文件。以前,Adwords 可以选择将数据导出为“普通 CSV”(可以使用 Ruby CSV 库进行解析),现在可以选择 Adwords CSV 或 Excel CSV。这两种格式都会导致此问题(以终端会话为例):

file = File.open('public/uploads/testfile.csv')
 => #<File:public/uploads/testfile.csv> 

file.read.encoding
 => #<Encoding:UTF-8> 

require 'csv'
 => true 

CSV.foreach(file) { |row| puts row }
ArgumentError: invalid byte sequence in UTF-8

让我们更改编码,看看是否有帮助:

file.close
 => nil 

file = File.open("public/uploads/testfile.csv", "r:ISO-8859-1")
 => #<File:public/uploads/testfile.csv> 

file.read.encoding 
=> #<Encoding:ISO-8859-1> 

CSV.foreach(file) { |row| puts row }
ArgumentError: invalid byte sequence in UTF-8

让我们尝试使用不同的 CSV 库:

require 'smarter_csv'
 => true 

file.close
 => nil 

file = SmarterCSV.process('public/uploads/testfile.csv')
ArgumentError: invalid byte sequence in UTF-8

这是一个没有双赢的局面吗?我必须推出自己的 CSV 解析器吗?

我正在使用 Ruby 1.9.3p374。谢谢!

更新 1:

使用 cmets 中的建议,这是当前版本:

file_contents = File.open("public/uploads/new-format/testfile-adwords.csv", 'rb').read

require 'iconv' unless String.method_defined?(:encode)
if String.method_defined?(:encode)
  file_contents.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '')
  file_contents.encode!('UTF-8', 'UTF-16')
else
  ic = Iconv.new('UTF-8', 'UTF-8//IGNORE')
  file_contents = ic.iconv(file_contents)
end

file_contents.gsub!(/\0/, '') #needed because otherwise, I get "string contains null byte (ArgumentError)"

CSV.foreach(file_contents, :headers => true, :header_converters => :symbol) do |row|
  puts row
end

这不起作用 - 现在我收到“文件名太长”错误。

【问题讨论】:

  • 你能提供一个你试图解析的文件的例子吗?
  • 你可以puts file.read 无一例外吗?
  • @benjaminjosephw 这是我正在使用的确切文件:jamesabbottdd.com/examples/testfile.csv
  • @majioa CSV.foreach(file) { puts file.read } 在同一行产生完全相同的错误:来自 ../.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/csv .rb:2058:in `=~'
  • @user906230 - 这有帮助吗? stackoverflow.com/a/8873922/2463468

标签: ruby parsing csv google-ads-api


【解决方案1】:

看着file in question

 $ curl -s http://jamesabbottdd.com/examples/testfile.csv | xxd | head -n3
0000000: fffe 4300 6100 6d00 7000 6100 6900 6700  ..C.a.m.p.a.i.g.
0000010: 6e00 0900 4300 7500 7200 7200 6500 6e00  n...C.u.r.r.e.n.
0000020: 6300 7900 0900 4200 7500 6400 6700 6500  c.y...B.u.d.g.e.

byte order markffee at the start 表明文件编码是小端 UTF-16,而每隔一个位置的 00 字节支持这一点。

这表明您应该能够做到这一点:

CSV.foreach('./testfile.csv', :encoding => 'utf-16le') do |row| ...

但是,这给了我来自 inside the CSV libraryinvalid byte sequence in UTF-16LE (ArgumentError)。我认为这是由于IO#getscalled in CSV时遇到BOM时由于某种原因只返回一个字节,导致UTF-16无效。

您可以通过使用bom|utf-16-le 作为编码来获取 CSV 以剥离 BOM:

CSV.foreach('./testfile.csv', :encoding => 'bom|utf-16le') do |row| ...

您可能更愿意将字符串转换为更熟悉的编码,在这种情况下您可以这样做:

CSV.foreach('./testfile.csv', :encoding => 'utf-16le:utf-8') do |row| ...

这两个似乎都可以正常工作。

【讨论】:

  • 不仅准确,而且很有教育意义。最佳工作 - 谢谢!
  • 这对我有用——似乎 Tableau(上帝帮助他们)以 UTF-16le 格式吐出 CSV,制表符分隔......只是。叹息。
【解决方案2】:

先将文件转换为 UTF8 再读取也很好用:

iconv -f utf-16 -t utf8 testfile.csv | ruby -rcsv -e 'CSV(STDIN).each {|row| puts row}'

Iconv 似乎正确理解该文件在开始时有一个 BOM,并在转换时将其剥离。

【讨论】:

    【解决方案3】:

    在处理 AdWords 关键字规划师下载时,这里有两件事需要解决。一是编码。

    $ file Keyword\ Stats\ 2019-02-12\ at\ 19_04_53.csv
    Keyword Stats 2019-02-12 at 19_04_53.csv: Little-endian UTF-16 Unicode text, with very long lines
    

    而且分隔符是制表符而不是逗号!

    所以单步遍历 CSV 文件就这么简单:

    CSV.foreach('Keyword Stats 2019-02-12 at 19_04_53.csv', col_sep: "\t", encoding: 'utf-16le:utf-8') do |row|
      puts row
    end
    

    仅供参考:\t 必须用双引号引起来,因此它会被解释为一个制表符,而不是字符串 \t

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-31
      • 2016-10-26
      • 2018-05-09
      • 2020-04-07
      • 2021-02-14
      相关资源
      最近更新 更多