Rubyのnilを返さないCSVライブラリ

RubyのCSVライブラリは何故か空フィールドについてnilを返します(「"」で括られていると空文字列を返します)。

require 'csv'

CSV.parse('a,,"","1,2"') do |row|
  p row
end
#=> ["a", nil, "", "1,2"]

なのでそれを考慮していないと NoMethodError(いわゆるぬるぽ)が起きたりして酷い目にあったりします (><)

require 'csv'

CSV.parse('a,,"","1,2"') do |row|
  row.each do |col|
    p col.length
  end
end
#=> 1
#=> test.rb:5:in `block (2 levels) in <main>': undefined method `length' for nil:NilClass (NoMethodError)

CSVの「"」は文字列中に「"」や「,」や改行が含まれる場合のエスケープ記号なので、「,"",」と「,,」は同じ値になるべきです。 「,"abc",」と「,abc,」はどちらも文字列abcになるのに、「,"",」と「,,」に違いがあるのがおかしいです。 「,"",」と「,,」とで異なる扱いをしたい場合なんてありません。

CSV ライブラリは変換器を設定しておくと値を変換できるという機能を持っているのですが、残念ながら nil に対しては働きません。1

CSV.parse_line("abc,,def", converters: ->(v){v.to_s.upcase})
#=> ["ABC", nil, "DEF"]

ということで、CSV をラップして nil の代わりに空文字列を返すようなライブラリを作ってみました。 ついでに CSV 生成時にも nil と "" の違いが無いようにしてあります。

github.com

require 'rightcsv'

CSV.parse('a,,"","1,2"')       #=> [["a", nil, "", "1,2"]]
RightCSV.parse('a,,"","1,2"')  #=> [["a", "", "", "1,2"]]

CSV.generate_line(['a', nil, '', '1,2'])       #=> 'a,,"","1,2"'
RightCSV.generate_line(['a', nil, '', '1,2'])  #=> 'a,,,"1,2"'

CSV が nil じゃなくて空文字列を返すようにするリクエスト Feature #12839 も発行されてますが、これが採用されたとしてもRubyの過去のバージョンでは使えないし、互換性維持のためにデフォルトでこの動きになることは考えにくいので、作ってみました。

このライブラリが不要になればそれに越したことはないです。

追記

最新の CSV gem だとオプションで対応していました。 残念ながらデフォルトではありませんが。


  1. これはバグ登録されていて Bug #11126、さっき見てみたら開発版で直されてました。