こんにちは、ryohei515です。
弊社が提供している Re:lation では LINE 等のチャットの CSV エクスポート機能があります。
そこで CSV::MalformedCSVError
というエラーが発生することがありました。
結論、掲題の通りの原因だったのですが、エラー調査時にこのことが書かれた記事等見かけることがなかったので、備忘で残しておこうと思います。
環境
Ruby 3.1.0
実際に発生したエラー
CSV.parse(csv)
の処理で、以下の通り発生しました。
/usr/local/lib/ruby/3.1.0/csv/parser.rb:1067:in `parse_quotable_robust': Any value after quoted field isn't allowed in line 1. (CSV::MalformedCSVError) from /usr/local/lib/ruby/3.1.0/csv/parser.rb:1007:in `block in parse_quotable_loose' from /usr/local/lib/ruby/3.1.0/csv/parser.rb:52:in `block in each_line' from /usr/local/lib/ruby/3.1.0/csv/parser.rb:49:in `each_line' from /usr/local/lib/ruby/3.1.0/csv/parser.rb:49:in `each_line' from /usr/local/lib/ruby/3.1.0/csv/parser.rb:963:in `parse_quotable_loose' from /usr/local/lib/ruby/3.1.0/csv/parser.rb:406:in `parse' from /usr/local/lib/ruby/3.1.0/csv.rb:2554:in `each' from /usr/local/lib/ruby/3.1.0/csv.rb:2554:in `each' from /usr/local/lib/ruby/3.1.0/csv.rb:2589:in `to_a' from /usr/local/lib/ruby/3.1.0/csv.rb:2589:in `read' from /usr/local/lib/ruby/3.1.0/csv.rb:1738:in `parse' ...
原因
試行した結果、CSV 内のデータ内の改行コードと、行の終わりの改行コードが統一されていれば正しく parse できるようだと判明しました。
以下の例だと、1行目2列目で fu\nga
として LF (\n
) で改行をし、行と行の区切りの改行としても LF を用いているため、問題なく parse できます。
irb(main):006:0> csv = "\"hoge\",\"fu\nga\"\n\"foo\",\"bar\"" => "\"hoge\",\"fu\nga\"\n\"foo\",\"bar\"" irb(main):007:0> CSV.parse(csv) => [["hoge", "fu\nga"], ["foo", "bar"]]
しかし、fu\r\nga
のように、データ内の改行を CRLF (\r\n
)、区切りの改行を LF といった形で混在させると CSV::MalformedCSVError
が発生しました。
irb(main):008:0> invalid_csv = "\"hoge\",\"fu\r\nga\"\n\"foo\",\"bar\"" => "\"hoge\",\"fu\r\nga\"\n\"foo\",\"bar\"" irb(main):009:0> CSV.parse(invalid_csv) Traceback (most recent call last): 16: from /usr/bin/irb:23:in `<main>' 15: from /usr/bin/irb:23:in `load' 14: from /Library/Ruby/Gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>' 13: from (irb):9 12: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv.rb:685:in `parse' 11: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv.rb:1245:in `read' 10: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv.rb:1245:in `to_a' 9: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv.rb:1236:in `each' 8: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv.rb:1236:in `each' 7: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv/parser.rb:303:in `parse' 6: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv/parser.rb:779:in `parse_quotable_loose' 5: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv/parser.rb:28:in `each_line' 4: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv/parser.rb:28:in `each_line' 3: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv/parser.rb:31:in `block in each_line' 2: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv/parser.rb:818:in `block in parse_quotable_loose' 1: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/csv/parser.rb:869:in `parse_quotable_robust' CSV::MalformedCSVError (Any value after quoted field isn't allowed in line 1.)
なお、改行コードを CRLF に統一していた場合も正常に動作するため、混在が NG なのかと思われます。
irb(main):010:0> crlf_csv = "\"hoge\",\"fu\r\nga\"\r\n\"foo\",\"bar\"" => "\"hoge\",\"fu\r\nga\"\r\n\"foo\",\"bar\"" irb(main):011:0> CSV.parse(crlf_csv) => [["hoge", "fu\r\nga"], ["foo", "bar"]]
おわりに
CSV として改行コードが混在してはいけないという決まりはないと思いますし、エラー内容からも原因を読み取れなかったため、特定に少し苦労しました。。
同じエラーで悩まれている方の参考になれば幸いです。