rubyのIOとエンコーディングについて
IOのドキュメントより
Kernel.#openのドキュメントより
第二引数のモードには、文字列では以下のパターンを受け付けている。
- "{mode}"
- "{mode}:{ext_enc}"
- "{mode}:{ext_enc}:{int_enc}"
modeはr/w/a/r+/w+/a+というオープンモードの指定。ext_encは外部エンコーディングの指定で、int_encは内部エンコーディングの指定。
File.open
Shift_JISで記述したCSVファイルをエンコーディングを指定してFile.openで読み込んだ場合。
$ cat encoding.rb File.open('sjis.csv', 'r:Shift_JIS:UTF-8') do |input| input.each_line do |line| puts line.encoding puts line end end $ ruby -v ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin11.0.1] $ ruby encoding.rb UTF-8 "あ","い","う","え","お"
CSV.foreach
CSV.foreachの:encodingオプションも同じだと思い試したが、例外が発生してしまう。
$ cat csv_encoding.rb require 'csv' CSV.foreach('sjis.csv', :encoding => 'Shift_JIS:UTF-8') do |row| puts row end $ ruby csv_encoding.rb /Users/koshigoe/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/csv.rb:2027:in `=~': invalid byte sequence in UTF-8 (ArgumentError) from /Users/koshigoe/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/csv.rb:2027:in `init_separators' from /Users/koshigoe/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/csv.rb:1570:in `initialize' from /Users/koshigoe/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/csv.rb:1335:in `new' from /Users/koshigoe/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/csv.rb:1335:in `open' from /Users/koshigoe/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/csv.rb:1201:in `foreach' from csv_encoding.rb:3:in `<main>'
CSVライブラリのソースを追いかけて、読み込みの実装を確認。改めて、File.openの確認のために以下を試す。
io = File.open('sjis.csv', 'r:Shift_JIS:UTF-8') data = io.read puts data.encoding io.rewind data = io.read(1024) puts data.encoding io.close
バイト数を指定せずにreadを実行すると内部エンコーディングに変換されるが、バイト数を指定するとASCII-8BITとなる。
で?
どうしたらいいんだ?File#readの仕様を把握してないので、次はそこを探ろう。
追記
引数 length が指定された場合はバイナリ読み込みメソッド、そうでない場合はテキスト読み込みメソッドとして 動作します。
なるほど。
追記
CSVの読み込み処理。
class CSV ... def read_io(bytes) @io.read(bytes).force_encoding(raw_encoding) end end
追記
CSVモジュールのread_ioメソッドを書き換えて、エンコーディング指定だけじゃなく変換までやるようにしてみた。どうするのが筋が良いのかは考えてない。
--- /Users/koshigoe/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/csv.rb 2011-10-19 00:23:20.000000000 +0900 +++ /Users/koshigoe/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/csv.rb.orig 2011-10-19 00:23:18.000000000 +0900 @@ -2315,7 +2315,7 @@ end def read_io(bytes) - @io.read(bytes).encode(raw_encoding, @io.external_encoding) + @io.read(bytes).force_encoding(raw_encoding) end end
外部エンコーディングだけ指定した場合、内部エンコーディングも指定した場合で期待通りの結果を得られた。
require 'csv' CSV.foreach('sjis.csv', :encoding => 'Shift_JIS') do |row| row.each{|col| puts col.encode('UTF-8') } end CSV.foreach('sjis.csv', :encoding => 'Shift_JIS:UTF-8') do |row| puts row end
次はRedmineで情報を探るべきだろうか。見逃される様なものじゃないと思うし、バグではないと思うんだけどよくわからんね。