概要
Ruby標準ライブラリのCSVの挙動についてのまとめ。
ruby 2.4.0 CSV の liberal_parsing オプションについて調査してみた のruby2.7.0バージョン。
csvの仕様
rubyの標準ライブラリのCSVパーサーではRFC4180準拠のパースが行われる。
こちらの仕様では、コンマの直後と次のコンマの直前に"
をおくことで、コンマを含んだ文字列を一つのフィールドとして扱うことができる。
CSV.parse('a,"b,c",d')
=> [["a", "b,c", "d"]]
ダブルクオートを含む文字列を表すためには、もう一つのダブルクオートでエスケープする必要がある。
CSV.parse('a,"b""c",d')
=> [["a", "b\"c", "d"]]
しかし、RFC4180はなかなか厳密な仕様で例えば a,b"c,d
のような文字列は不正なcsvになり、例外が発生する。
CSV.parse('a,b"c,d') # CSV::MalformedCSVError (Illegal quoting in line 1.)
一方で上記のようなフォーマットは頻繁に出てきうるため、これを厳密にエラーにしていると取り扱いが難しい場合がある。そのために実用的なパースを行うためのliberal_parsing
というオプションがruby2.4.0から導入されている。
上記の記事ではruby2.4を使ってliberal_parsingの挙動について調査を行っているが、ruby2.7で確認したところ挙動が変わっていたところも多くあったのでここでまとめておく。全般的にruby2.7の方が挙動が直感的になっている。
liberal_parsing のruby2.7での挙動
ダブルクォートに囲まれたフィールドの場合
正しくエスケープされたダブルクォートの場合
CSV.parse('a,"bb""b",c', liberal_parsing: true)
=> [["a", "bb\"b", "c"]]
文中にエスケープされていないダブルクォートがある場合
CSV.parse('a,"bb"b",c', liberal_parsing: true)
=> [["a", "\"bb\"b\"", "c"]]
ruby2.4では例外は発生していた。
ダブルクォートに囲まれていないフィールドの場合
途中にダブルクォートがある場合
SV.parse('a,bb"b,c', liberal_parsing: true)
=> [["a", "bb\"b", "c"]]
途中にエスケープされたダブルクォートがある場合
CSV.parse('a,bb""b,c', liberal_parsing: true)
=> [["a", "bb\"\"b", "c"]]
先頭にのみダブルクォートがある場合
CSV.parse('a,"bbb,c', liberal_parsing: true)
# CSV::MalformedCSVError (Unclosed quoted field in line 1.)
ruby2.4と同様に例外。
先頭と途中にダブルクォートがあって、ダブルクォートの合計が偶数の場合
CSV.parse('a,"bb"b,c', liberal_parsing: true)
=> [["a", "\"bb\"b", "c"]]
先頭と途中にダブルクォートがあって、ダブルクォートの合計が奇数の場合
CSV.parse('a,"b"b"b,c', liberal_parsing: true)
=> [["a", "\"b\"b\"b", "c"]]
ruby2.4では例外が発生していたが、ruby2.7ではパースできるようになった。
末尾と途中にダブルクォートがある場合
CSV.parse('a,bb"b",c', liberal_parsing: true)
=> [["a", "bb\"b\"", "c"]]
ruby2.4と同様にパースできる。