IronRuby 1.0 におけるエンコーディングの取り扱いについて

前回にご案内した勉強会というかセミナーに絡んで IronRuby のマルチバイト文字の扱い方を色々と調べています。特に nkf ライブラリが未完なので文字列の取り扱いがどうなのかという点が気になっていました。検索してみると Jamzzの日々というはてなダイアリーで、nkfが未完ならnkfを何とか使えるようにしようという取り組みが見つかりました。
以下に、.NET Framework 2.0 用の IronRuby で気が付いたことを記載します。

  • ir.exe では putsメソッドはシフトJISで文字を出力しているようだ。
    というのは、UTF-8 や EUC などでは文字が化けるからです。
  • スクリプトファイルは、SJIS、UTF-8、EUCに対応しているけど、ir.exeからは $KCODEを設定しないと正しいエンコードを認識しない。 SJISは、何もしないでもOK。
  • Fileオブジェクトは、$KCODEに関わらずSJIS以外はバイナリとして扱っている(ASCII-8BIT)。
    UTF-8などだとBOMもバイナリとして読み込んでしまっていました。
  • $KCODE のdefault は、'NONE'になっている。

試しにUTF-8でBOMを持つファイルを読み込むコードを以下のようにしてみました。

 # オリジナルの読み込み(UTF-8)
d0 = open("utf8 bom.txt").read
# BOM(0xEF 0xBB 0xBF)を除いた文字列の作成
d1 = d0.slice 3..-1
# エンコードをUTF-8にした文字列の作成
d2 = d1.ToString System::Text::Encoding.UTF8 
# 正常に出力できる(puts は化けます)
System::Console.write_line d2

このコードでの注意点は、「ToString」メソッドです。.NET Frameworkをご存じの方にはメソッド名は馴染みのあるものですが、エンコーディングを指定している点に「 」が付くことでしょう。これは IronRuby の Stringオブジェクトが MutableStringクラスを使っているので、ソースコードをチェックしていて見つけたものです。引数に指定するエンコーディングは、対象の文字列を表しているエンコードになり System::Text::Encodingクラスのインスタンスになります。このメソッドを呼び出すと、「UTF-8 のエンコーディングを持つ Stringクラスのインスタンス」が作成されます。誤解を招きそうなので別の例として、「ToString System::Text::Encoging.get_encoding('shift-jis')」をあげます。この場合であっても UTF-8 のStringオブジェクトが返ります(この例では、to_clr_string メソッドでも同じになります。なぜなら、SJIS を UTF-8に変換するからで、IronRuby では clr_string を UTF-8 として扱っているからです )。
というのは、IronRuby のString オブジェクトは Ruby 1.9x系と同じように文字列オブジェクト毎にエンコーディング情報を持っているからです。このエンコーディング情報を確認するには、「Encoding」プロパティを使用します。Encodingプロパティは、RubyEncodingクラスのインスタンスを返します。

ここまで来たら、やっぱり putsメソッドで出力したくなります。これを行うためには文字列をUTF-8からSJISへ変換する必要があります。でも nkf が未完なので使用できません。なので、.NET Framework のライブラリを使って書いたのが以下のコードです。

 # shift-jisへ変換します
z = System::Text::Encoding.convert 
              System::Text::Encoding.UTF8,
              System::Text::Encoding.get_encoding('shift-jis'),
              d2.ToByteArray
# SJISの空文字列を作成します
s = String.CreateMutable("".Encoding)
# 文字列にデータを格納します(バイト配列)
s.Append z
# これで正常に出力できます
puts s

上記のコードは、以下のようなことをしています。

  • System::Text::Encoding.Convert メソッドで SJIS へ変換(戻り値はバイト配列)
    ToByteArray メソッドは文字列のバイト配列を返します。
  • String.CreateMutable メソッドにエンコーディングを指定すれば、指定したエンコーディングを持つ空の文字列が作成できます。
    "".Encoding は defaultの shift-jis エンコーディングです。
  • インスタンスメソッドの String#Append でバイト配列を追加します。

 

これでシフトJISのエンコーディングを持つStringオブジェクトができますので、putsメソッドで無事に出力できるようになったということです。

最後に Silverlight で動作する IronRuby は .NET Framework のサブセット版で機能が少なくなっています。このためもあるのでしょうが、 default のエンコーディングが UTF-8 になってます。また Silverlight では シフトJISなどをサポートしていませんので UTF-8やUTF-16 以外を使う場合は工夫する必要があります。これは、IronRuby の制限ではなく Silverlight 側の制限になっています。

追記:テキストファイルの改行コードでCR+LFがあると思うのですが、上記のFile#readメソッドで確認したら CR は削除されて LF だけになっていました。理由は知りませんが、UNIX系が LF なので合わせたように感じています。