Rubyで日本語の入ったcsv(utf-8)を使おうと思ったらinvalid byte sequence in Windows-31Jエラーが出たのでメモ

csvを取り込んでrubyのhashにしたりjsonをつくろうと思って何気なくcsvファイルを作って簡単に取り込むコードを書いてみたらinvalid byte sequence in Windows-31J (ArgumentError)というエラーが出ました。ちなみに書いたコードがコチラ。

require "csv"
CSV.open("test.csv", "r") do |row|
  puts row
end

一応csvファイルは、

item,price,value
りんご,500,1
ばなな,80,5

ふむ。rbファイルの先頭行に魔法のことば(coding: utf-8)はあるし、もちろんutf-8で保存してるし、簡単に作ったcsvもutf-8。

で、色々調べていくうちにStringのインスタンスがEncoding情報を持つようになったという情報を読んで、あぁそういえばエンコーディングを調べるメソッドがあったなと思ってcsvファイルのエンコーディングを調べてみる。

p open("test.csv", "r").read.encoding 

で結果がEncoding:Windows-31J、なるほど意味が分からない。え?なんでutf-8じゃないの?

で、色々調べたり考えたりしたんだけど、csvってそもそも勝手にShift-JIS(Windows-31J)になっちゃうとか、っていうか他人に使ってもらうと考えた場合csvファイルをExcelで作ってくるとしたら自然にShift-JISになるよねという解釈に落ち着いたのでcsvファイルをShift-JISで作る事に。

そうするとWEBで見つかるコードが全部参考になった。結論はcsvはShift-JISでいこう。ExcelはShift-JISのcsvしか読めないみたいだし。

というわけで、csvをShift-JISにしたら思い通りの事ができた。

require "csv"
 
data =  CSV.table("test.csv", encoding: "SJIS:UTF-8")
 
print data.headers   # =>[:item, :price, :value]
puts data[:item]     # =>りんご ばなな
puts data[:price][0] # =>500

ちなみにCSV.tableはCSV.read(‘data.csv’, headers:true, converters: :numeric, header_converters: :symbol)と一緒なので、csvの先頭行が日本語の場合はsymbolにできないのでおそらく動かない。先頭行が日本語のデータは普通にCSV.openの方が良いので使い分ける事。アクセスしやすいのでCSV.tableが好きなのだけど。

参考

この記事を書いた時のruby -v:ruby 1.9.3p448