ファイル入出力とエンコーディング

Ruby1.8 の文字列はただのバイト列だったが、Ruby1.9 からは、それぞれの文字列がエンコーディングを持っている。
エンコーディングの変換には String#encode を使う。

utf8 = 'Ruby関西'
eucjp = utf8.encode(Encoding::EUC_JP)

utf8.encoding       # => #<Encoding:UTF-8>
eucjp.encoding      # => #<Encoding:EUC-JP>

ファイルとスクリプトエンコーディングが一致

スクリプトエンコーディングと入力ファイルのエンコーディングが一致していれば、何も悩むことはない。

open(fn){|file| ...

ファイルのエンコーディングを指定

スクリプトエンコーディングと入力ファイルのエンコーディングが一致していなければ、読み込んだ文字列は恐らく文字化けすることになる。

入力ファイルのエンコーディングがあらかじめ分かっているなら、それを指定すればよい。

open(fn, 'r:EUC-JP'){|file| ...

上記は日本語 EUC のファイルから読み込む例。
この場合、読み込んだ文字列のエンコーディングは Encoding::EUC_JP となる。

File#open の第2引数で指定した 'r' はモード。'r' は読み込み、'w' は書き込み、など。

モードにつづく ':エンコーディング名' は外部エンコーディングの指定。

読み込んだ文字列をスクリプトエンコーディングに変換したいなら、内部エンコーディングも指定すれば可能。

open(fn, "r:EUC-JP:#{__ENCODING__}") {|file| ...

__ENCODING__ はスクリプトエンコーディング
File#open に指定するのは、エンコーディングの名前 (Encoding#to_s) らしい。

ファイルのエンコーディング不定

スクリプト起動時に人がファイルのエンコーディングを指定する場合は、下手な作り込みは不要。

以下スクリプトは与えられたファイルを UTF-8, 日本語 EUC, Windows-31J に変換して出力する。

ARGV.each do |fn|
  [Encoding::UTF_8, Encoding::EUC_JP, Encoding::CP932].each do |enc|
    open("#{fn}_#{enc.to_s}_#{Encoding.default_external}_#{Encoding.default_internal}", "w:#{enc}") do |f|
      f.puts open(fn, &:read)
    end
  end
end

これを enc.rb という名前で保存した場合、以下のように実行する。

$ ruby -Eeuc-jp enc.rb euc.txt

上の例では ruby の -E オプションでエンコーディングを指定。
すると Encoding.default_external が Encoding::EUC_JP になって、指定がなければ入出力は日本語 EUC になる。*1

あまり使い道なさそうだけど、-Eeuc-jp:euc-jp のように Encoding.default_internal も指定できる。

Encoding.default_external を環境変数で制御することもできる。

$ LANG=ja_JP.UTF-8 ./enc.rb utf.txt
$ LANG=ja_JP.EUCJP ./enc.rb euc.txt
$ LANG=ja_JP.SJIS  ./enc.rb sjis.txt

なんか ja_JP.SJIS は違う気もする ……

*1:ファイルが UTF-8 なら -Eutf-8、ファイルが Windows-31J なら -Ecp932 のように指定。