Ruby の CSV ライブラリはとても便利なんだけど、ひとつだけ問題があって、CSV をパースしたときに nil
を返すことがある。
つぎのような CSV をパースすると2番目のカラムが nil
になる。4番目のカラムは空文字になるのに!
hoge,,fuga,"",piyo
require 'csv' CSV.parse_line('hoge,,fuga,"",piyo') #=> ["hoge", nil, "fuga", "", "piyo"]
なので CSV のパース結果は全部文字列だと思って使ってるとエラーになってびっくりする。
CSV.parse_line('hoge,,fuga').map(&:upcase) #=> undefined method 'upcase' for nil (NoMethodError)
CSV を生成するときも nil
と空文字で変わる:
puts ['hoge', nil, 'fuga', '', 'piyo'].to_csv #=> hoge,,fuga,"",piyo
CSV で "
でクォートする必要があるのは、値に "
や ,
や改行が含まれてる場合だけなので、空文字を ""
と書く必要なんてないし、CSV 的に ,,
と ,"",
には違いがない。
なのに何故こんな変な動きになってしまっているのか。誰も嬉しくないと思うんだけども。
いちおうオプションがあって、nil_value
とか quote_empty
を指定すればまともになる。
CSV.parse_line('hoge,,fuga,"",piyo', nil_value: '') #=> ["hoge", "", "fuga", "", "piyo"] puts ['hoge', nil, 'fuga', '', 'piyo'].to_csv(quote_empty: false) #=> hoge,,fuga,,piyo
けど、いちいち指定するのが面倒なので、CSV のデフォルト動作を変更するための gem を作った。
Gemfile に
gem 'sane_csv'
と書くか、
$ gem install sane_csv
して
require 'sane_csv'
すればオプションを指定しなくてもまともな動きをする。
require 'sane_csv' CSV.parse_line('hoge,,fuga,"",piyo') #=> ["hoge", "", "fuga", "", "piyo"] puts ['hoge', nil, 'fuga', '', 'piyo'].to_csv #=> hoge,,fuga,,piyo
まあデフォルト値を変えただけなんだけども。Ruby の動的さすばらしい。
CSV の動きを変えるのでプログラム全体に影響を与えるけど、元の動きに依存してるものなんてないだろうからいいんじゃないかな(しらんけど