【问题标题】:Ruby unable to parse a CSV file: CSV::MalformedCSVError (Illegal quoting in line 1.)Ruby 无法解析 CSV 文件:CSV::MalformedCSVError(第 1 行中的非法引用。)
【发布时间】:2013-05-22 06:57:21
【问题描述】:

Ubuntu 12.04 LTS

Ruby ruby​​ 1.9.3dev(2011-09-23 修订版 33323)[i686-linux]

Rails 3.2.9

以下是我收到的 CSV 文件的内容:

"date/time","settlement id","type","order id","sku","description","quantity","marketplace","fulfillment","order city","order state","order postal","product sales","shipping credits","gift wrap credits","promotional rebates","sales tax collected","selling fees","fba fees","other transaction fees","other","total"
"Mar 1, 2013 12:03:54 AM PST","5481545091","Order","108-0938567-7009852","ALS2GL36LED","Solar Two Directional 36 Bright White LED Security Flood Light with Motion Activated Sensor","1","amazon.com","Amazon","Pasadena","CA","91104-1056","43.00","3.25","0","-3.25","0","-6.45","-3.75","0","0","32.80"

但是,当我尝试解析 CSV 文件时出现错误:

1.9.3dev :016 > options = { col_sep: ",", quote_char:'"' }
=> {:col_sep=>",", :quote_char=>"\""} 

1.9.3dev :022 > CSV.foreach("/tmp/my_data.csv", options) { |row| puts row }
CSV::MalformedCSVError: Illegal quoting in line 1.
    from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1925:in `block (2 levels) in shift'
    from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1887:in `each'
    from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1887:in `block in shift'
    from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1849:in `loop'
    from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1849:in `shift'
    from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1791:in `each'
    from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1208:in `block in foreach'
    from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1354:in `open'
    from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1207:in `foreach'
    from (irb):22
    from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/bin/irb:16:in `<main>'

然后我尝试简化数据,即

"name","age","email"
"jignesh","30","jignesh@example.com"

但是我仍然遇到同样的错误:

      1.9.3dev :023 > CSV.foreach("/tmp/my_data.csv", options) { |row| puts row }
  CSV::MalformedCSVError: Illegal quoting in line 1.
      from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1925:in `block (2 levels) in shift'
      from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1887:in `each'
      from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1887:in `block in shift'
      from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1849:in `loop'
      from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1849:in `shift'
      from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1791:in `each'
      from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1208:in `block in foreach'
      from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1354:in `open'
      from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/lib/ruby/1.9.1/csv.rb:1207:in `foreach'
      from (irb):23
      from /home/jigneshgohel/.rvm/rubies/ruby-1.9.3-rc1/bin/irb:16:in `<main>'

我再次尝试像这样简化数据:

name,age,email
jignesh,30,jignesh@example.com

它有效。请参阅下面的输出:

  1.9.3dev :024 > CSV.foreach("/tmp/my_data.csv") { |row| puts row }
  name
  age
  email
  jignesh
  30
  jignesh@example.com
   => nil 

但我将收到包含引用数据的 CSV 文件,因此我实际上并不是在寻找删除引号解决方案。我无法找出导致错误的原因:CSV::MalformedCSVError: Illegal quoting in line 1. 在我之前的示例中。

我已经通过在我的文本编辑器中启用“显示空白字符”和“显示行尾”来验证 CSV 中没有前导/尾随空格。我还使用以下方法验证了编码。

  1.9.3dev :026 > File.open("/tmp/my_data.csv").read.encoding
  => #<Encoding:UTF-8> 

注意:我也尝试使用 CSV.read,但该方法出现同样的错误。

谁能帮我摆脱这个问题,让我明白哪里出了问题?

======================

我刚刚在http://www.ruby-forum.com/topic/448070 找到了以下帖子并尝试了以下内容:

  file_data = file.read
  file_data.gsub!('"', "'")
  arr_of_arrs = CSV.parse(file_data)

  arr_of_arrs.each do |arr|
    Rails.logger.debug "=======#{arr}"
  end

得到以下输出:

   =======["\xEF\xBB\xBF'date/time'", "'settlement id'", "'type'", "'order id'", "'sku'", "'description'", "'quantity'", "'marketplace'", "'fulfillment'", "'order city'", "'order state'", "'order postal'", "'product sales'", "'shipping credits'", "'gift wrap credits'", "'promotional rebates'", "'sales tax collected'", "'selling fees'", "'fba fees'", "'other transaction fees'", "'other'", "'total'"]
    =======["'Mar 1", " 2013 12:03:54 AM PST'", "'5481545091'", "'Order'", "'108-0938567-7009852'", "'ALS2GL36LED'", "'Solar Two Directional 36 Bright White LED Security Flood Light with Motion Activated Sensor'", "'1'", "'amazon.com'", "'Amazon'", "'Pasadena'", "'CA'", "'91104-1056'", "'43.00'", "'3.25'", "'0'", "'-3.25'", "'0'", "'-6.45'", "'-3.75'", "'0'", "'0'", "'32.80'"]

由于默认使用的 col_sep 是逗号字符,因此无法正确读取数据。 但是我尝试像这样使用 quote_char 选项:

  arr_of_arrs = CSV.parse(file_data, :quote_char => "'")

但最终出现以下错误:

   CSV::MalformedCSVError (Illegal quoting in line 1.):

谢谢, 吉格尼什

【问题讨论】:

  • 使用了您提供的示例数据并且解析工作正常。没有收到任何CSV::MalformedCSVError: Illegal quoting in line 1 错误。
  • 在我编辑的部分中,输出包含以下内容:“\xEF\xBB\xBF'date/time'”。它会产生一些问题吗?我不知道它代表什么。谢谢。
  • 文件开头的 Unicode 字符是 BOM(字节顺序标记)。你可以试试sub!(/^\xEF\xBB\xBF/, '')CSV.foreach("test.csv", encoding: "bom|utf-8")
  • 谢谢阿南德,我将尝试使用您建议的编码解决方案。同时在使用 header_converters 时在编辑部分使用我的临时解决方案,例如:arr_of_arrs = CSV.parse(file_data, { col_sep: ";", headers: true , header_converters: [ :symbol ] }) 我收到以下错误:Encoding::UndefinedConversionError ("\xEF" from ASCII-8BIT to UTF-8). 那一个提到 ASCCII-8BIT 作为编码。该编码如何重要,那些如何BOM 字符进入了那里?此类错误应该清楚地显示在库抛出的异常中,而不是在 to_s 输出中偶然发现它们。
  • 以下链接joelonsoftware.com/articles/Unicode.html,将有助于理解编码的重要性。至于这些 BOM 字符是如何进入的,您需要检查收到的 CSV 文件的来源以及它是如何保存的。

标签: ruby csv malformed


【解决方案1】:

此错误的一个不太常见的原因是文件没有执行 任何 字段引用,但仍设置了 quote_char(默认为 ")并且发生了一个或多个字段包含字符。

要完全禁用字段引用,请在解析选项中设置quote_char: nil

例如,给定一个文件/tmp/people.csv,如下所示:

Actor,Dwayne "The Rock" Johnson,1972-05-02
Character,TV's Frank,1956-08-30

可以这样解析:

CSV.read('/tmp/people.csv', quote_char: nil)

【讨论】:

    【解决方案2】:

    :liberal_parsing =&gt; true 参数添加到CSV.read,这应该可以解决“非法引用”的一些问题

    【讨论】:

      【解决方案3】:

      Rails 6 版本,ruby 2.4+

      CSV.foreach(file, liberal_parsing: true, headers: :first_row) do |row|
          // do whatever
      end
      

      https://ruby-doc.org/stdlib-2.4.0/libdoc/csv/rdoc/CSV.html

      【讨论】:

        【解决方案4】:

        来自this thread 传递选项:quote_char =&gt; "|"

        CSV.read(filename, :quote_char => "|")
        
        

        【讨论】:

        • ? 这似乎也适用于 CSV.foreach 方法
        • 它可以工作,但与 headers: true 或 headers: :first_row 结合使用会混淆列值
        【解决方案5】:

        我尝试读取文件并获取一个字符串,然后将这些字符串解析为 CSV 表,但收到异常:

        CSV.read(File.read('file.csv'), headers: true)
        CSV::MalformedCSVError: Unclosed quoted field on line 1794.
        

        这里提供的答案都不适合我。事实上,得票最高的那个花了很长时间来解析,最终我终止了执行。它很可能引发了许多异常,而且对于大文件而言,时间成本很高。

        更成问题的是,该错误没有太大帮助,因为它是一个大型 CSV 文件。 1794 行到底在哪里?我在 LibreOffice 中打开了该文件,该文件打开时没有任何问题。第 1794 行是 csv 文件的最后一行数据。所以显然问题与 CSV 文件的结尾有关。我决定使用 File.read 将内容作为字符串进行检查。我注意到字符串以回车符结尾:

        ,\"\"\r
        

        我决定使用 chomp 并删除文件末尾的回车符。请注意,如果 $/ 没有从默认的 Ruby 记录分隔符更改,则 chomp 还会删除回车符(即它将删除 \n、\r 和 \r\n)。

        CSV.parse(File.read('file.csv' ).chomp, headers: true)
         => #<CSV::Table mode:col_or_row row_count:1794>
        

        它奏效了。问题是文件末尾的 \r 字符。

        【讨论】:

          【解决方案6】:

          Anand,感谢您提供编码建议。这为我解决了非法引用问题。

          注意:如果您希望迭代器跳过标题行添加headers: :first_row,如下所示:

          CSV.foreach("test.csv", encoding: "bom|utf-8", headers: :first_row)
          

          【讨论】:

          • 谢谢! encoding: "bom|utf-8" 解决了我的问题。
          • 对于那些使用 ruby​​ 2.4+ 获得 ArgumentError: unknown encoding name - bom|utf-8 的用户,请确保将 csv gem 更新到版本 3 或更高版本(Gemfile 中的 gem 'csv', '~&gt; 3.0')。
          【解决方案7】:

          我遇到了引发此错误的商标字符的问题。

          商标字符在 UTF-8 中转换为 \"!,因此引发错误的是开放式引号符号。所以我这样做了:

          .gsub!("\"!", "")

          然后我尝试创建我的 CSV 对象,它工作正常。

          【讨论】:

            【解决方案8】:

            试试这个提示:

            1. 在文本编辑器中打开您的 CSV 文件
            2. 选择整个文件并复制它
            3. 打开一个新的文本文件
            4. 将 CSV 数据粘贴到新文件中并保存新文件
            5. 导入新的 CSV 文件

            【讨论】:

            • csv文件可能是MB,不能这样打开
            【解决方案9】:

            我刚遇到这样的问题,发现 CSV 不喜欢 col-sep 和引号字符之间的空格。 一旦我删除了这些,一切都很好。 所以我有:

            12,  "N",  12, "Pacific/Majuro"
            

            但是一旦我用 gsubed 了空格

            .gsub(/,\s+\"/,',\"')
            

            导致

            12,"N",  12,"Pacific/Majuro"
            

            一切顺利。

            【讨论】:

            • 注意是否要替换逗号值内引号字符串两边的空格...gsub(/,\s+\"/,',"').gsub(/\"\s+ ,/,'",')
            【解决方案10】:
            quote_chars = %w(" | ~ ^ & *)
            begin
              @report = CSV.read(csv_file, headers: :first_row, quote_char: quote_chars.shift)
            rescue CSV::MalformedCSVError
              quote_chars.empty? ? raise : retry 
            end
            

            它并不完美,但它在大多数情况下都有效。

            注意CSV.parse 采用与CSV.read 相同的参数,因此可以使用文件或内存中的数据

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-09-13
              • 1970-01-01
              • 2014-05-25
              • 2015-05-31
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多