以前書いた記事(Ruby 1.9 の新機能を調べてみた)の m17n がらみの箇所についてコメントやらトラックバックやらをいただいたので、もう少し調べてまとめてみた。

なお、1.9.0 リリース版ではなく、開発版(trunk r14835)で動作を確認している。

IO クラス

全般 IO オブジェクトは、それぞれ外部エンコーディング(external encoding)と内部エンコーディング(internal encoding)を持っている。 外部エンコーディング、内部エンコーディングを適切に設定しておくことで、入出力する文字列を自動的に変換(transcode)することができる。(後述)

IO.open

IO.popen

Kernel#open 第2引き数(mode)で、" r:UTF-8 " または " r:UTF-8:EUC-JP "のようにコロン区切りでエンコーディング名を指定することにより IO オブジェクトのエンコーディングを設定できる。 エンコーディングを1つ指定した場合、それが外部エンコーディングとして設定される。 エンコーディングを2つ指定した場合、1つ目が外部エンコーディングとして、2つ目が内部エンコーディングとして設定される。 書き込みモードの時も同様に指定できる。 読み出しモードで外部エンコーディングを指定せずに IO オブジェクトを作成した場合、外部エンコーディングとして Encoding.default_external が設定される。 IO のサブクラスの場合は挙動が変わるかも。

IO#getc

IO#ungetc 1文字を読み出す、または戻す。(多バイト文字も考慮される)

IO#external_encoding IO オブジェクトの外部エンコーディングを Encoding で返す。 設定していない場合は nil が返る。

IO#internal_encoding IO オブジェクトの内部エンコーディングを Encoding で返す。 設定していない場合は nil が返る。

IO#set_encoding(enc)

IO#set_encoding(ext_enc, int_enc) IO オブジェクトのエンコーディングを変更する。引き数には文字列または Encoding オブジェクトを指定する。 IO オブジェクト(自分自身)を返す。 引き数が 1 つの Encoding オブジェクトの場合、外部エンコーディングを設定する。 引き数が 1 つの文字列の場合、" 外部エンコーディング名 " として外部エンコーディングを指定するか、もしくは " 外部エンコーディング名:内部エンコーディング名 " のようにコロン区切りで外部/内部エンコーディング名を同時に指定する。 引き数が 2 つの場合、1 つめに外部エンコーディング、2 つめに内部エンコーディングを指定する。



$stdin .set_encoding( ' UTF-8:Shift_JIS ' ) p $stdin .internal_encoding p $stdin .external_encoding

エンコーディング変換 (transcode) 読み出し時 IO#internal_encoding が nil の場合、読み出し結果の文字列のエンコーディングは IO#external_encoding になる。エンコーディングの変換(transcode)はされない。 外部エンコーディング(external encoding)を明示的に指定していない場合、 Encoding.default_external が使われる(デフォルト動作)。



p Encoding .default_external open( ' filename ' , ' r ' ) do | f | p f.internal_encoding p f.external_encoding p f.read.encoding end open( ' filename ' , ' r:EUC-JP ' ) do | f | p f.internal_encoding p f.external_encoding p f.read.encoding end

IO#internal_encoding が nil 以外の場合、読み出した文字列を external_encoding から internal_encoding に変換した結果が返る。



p Encoding .default_external open( ' filename ' , ' r:EUC-JP:UTF-8 ' ) do | f | p f.internal_encoding p f.external_encoding p f.read.encoding end

書き込み時 IO#internal_encoding と IO#external_encoding がともに nil の場合、エンコーディングの変換(transcode)は行なわれずにそのまま出力される。(デフォルト動作) IO#internal_encoding が nil で IO#external_encoding が設定されている場合、文字列のエンコーディングを external_encoding に変換した結果を出力する。(内部では String#encode が呼ばれる)



str = "\xa4\xa2" .force_encoding( ' EUC-JP ' ) p $stdout .internal_encoding p $stdout .external_encoding $stdout .set_encoding( ' UTF-8 ' ) p $stdout .internal_encoding p $stdout .external_encoding puts str

IO#internal_encoding と IO#external_encoding がともに設定されている場合、文字列が持っているエンコーディング情報に関わらず、エンコーディングを internal_encoding から external_encoding へ変換した結果を出力する。(内部では String#encode が呼ばれる)



str = "\xa4\xa2" .force_encoding( ' ASCII ' ) $stdout .set_encoding( ' UTF-8 ' , ' EUC-JP ' ) p $stdout .internal_encoding p $stdout .external_encoding p str.encoding puts str

TODO 一旦設定した外部/内部エンコーディングを nil に戻すことはできるか?





まだ理解してない部分、抜けてる部分、挙動が確定してなさそうな部分(バイナリモードの IO でエンコーディング指定するとどうなるの? とか)があるので、気づいたら徐々に穴埋めするかも。