Ruby 1.9.2 CSV でInternal Encodingを指定するとおかしくなる件

今日これで数時間潰してしまったのでメモ。

Ruby 1.9のCSVでShift-JISのファイルを読み込むときのバグです。

[ruby]
# encoding: UTF-8

require ‘csv’

CSV.foreach(‘test.csv’, row_sep: “n”, encoding: “SJIS:UTF-8”) do |row|
puts row.join(‘:’)
end

CSV.foreach(‘test.csv’, encoding: “SJIS:UTF-8”) do |row|
puts row.join(‘:’)
end
[/ruby]

ファイル”test.csv”は以下のもので、行末は”n”でエンコーディングはShiftJISで保存してあります。

[text]
今日は,2月末なのに,東京でも,大雪でした

[/text]
ruby 1.9.2-p290で実行すると

[text]
NaoAir:Desktop nao$ ruby test.rb
今日は:2月末なのに:東京でも:大雪でした
/Users/nao/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/csv.rb:2027:in =~': invalid byte sequence in UTF-8 (ArgumentError)
from /Users/nao/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/csv.rb:2027:in
init_separators’
from /Users/nao/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/csv.rb:1570:in initialize'
from /Users/nao/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/csv.rb:1335:in
new’
from /Users/nao/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/csv.rb:1335:in open'
from /Users/nao/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/csv.rb:1201:in
foreach’
from test.rb:19:in `’

[/text]

のようになります。最初の”row_sep”を指定したものはうまくいきますが、”row_sep”を指定していない方はエラーが出ます。

理由は”row_sep”で行末記号を指定してあげないと、CSVは自分で先読みをして行末記号を推定しようとしますが、この処理がエンコーディングによってはバグを起こすようです。

解決策は最初の例のように行末記号をしてあげる、そもそも自動推定をさせないこと。あるいはCSVにtranscodeさせるのをやめ、CSVから得られた個々の結果を個別に#encodeしてあげること(下例)。
[ruby]
CSV.foreach(‘test.csv’, encoding: “SJIS”) do |row|
puts row.map{|c| c.encode(‘UTF-8’)}.join(‘:’)
end
[/ruby]

あるいはRuby 1.9.3ではこのバグは修正されているようですので、1.9.3にアップグレードすれば問題なく処理されます。

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s