Ruby1.9.1と文字列エンコード再び

DXRubyのエンコード関連を改善したいと思い、いろいろ勉強しているのでとりあえずまとめ。


Ruby1.8系では、文字列はバイナリであり、エンコードを持たない。
ファイルなどから読み込んだ文字列がなんのエンコードなのかもわからないから、ライブラリを使って自動判別を試みたり、決めウチしたりすることになる。
拡張ライブラリを作るときは、ユーザーが使うエンコードを指定する必要がある。
DXRubyはWindowsAPIがCP932(Windows-31Jとも言う。厳密には違うが一般的にはShiftJISで問題ない)となっているから、ShiftJISを使ってください、とお願いしている。
RGSSはUTF8だが、これはおそらく内部で変換しているのだと思う。Win32に変換APIあるし。
DXRubyでWindow.encoding=UTF8とかの指定で内部変換するような手も検討している。


さて、Ruby1.9.1では文字列オブジェクトがエンコードを持つようになった。
String#encodeとかString#force_encodingを基点にした解説はあちこちで見るから、ここはちょっと違う切り口で行こう。


Ruby1.9.1にはEncoding.default_externalというものがあって、外部から読み込んだ文字列になんのエンコードを想定するか、という指定ができる。
これを指定しておくと、読み込んだ文字列にそのエンコードをとりあえず無理矢理くっつけて
くれる。
Encoding.default_externalのデフォルトは-Eや-Kで指定したものか、なければロケールとなる。
また、Encoding.default_internalというのもあって、こちらはデフォルトの内部エンコードを指定する。
これを指定しておくと、Encoding.default_externalで指定したエンコードをくっつけてからEncoding.default_internalに変換して入力される。
初期値はnilで、nilだと変換しない。


この仕組みの何がよいかというと、拡張ライブラリを作るときにユーザーにエンコードを指定しなくてよくなるところがよい。
DXRubyを例に考えると、Windowsの場合、ロケールはWindwos-31Jとなっているのではなかろうかと思うから、ユーザーにはお好きなエンコードスクリプトを書いたりファイルを読み書きして頂いて、文字列をDXRubyが受け取ったらロケールに変換してAPIに渡す。
APIから受け取った文字列はロケールをくっつけてdefault_internalに変換してスクリプトに渡す。
こうすることで、ユーザーへのエンコードの指定が必要なくなるし、外国のロケールが違うWindows(タイ語Windowsとか)にもそのまま対応できるようになる。
こう考えるとこの部分は確かに理想的だ。


ところでロケールはEncoding.locale_charmapで取得できるらしい。これがRubyで認識できない場合はASCII-8BITがEncoding.default_externalになるとのこと。
確認してみた。
環境はruby 1.9.1p243 (2009-07-16 revision 24175) [i386-mswin32]

p Encoding.locale_charmap # => "CP0"
p Encoding.default_external # => #<Encoding:ASCII-8BIT>

CP0ってなんだろう・・・?
WindowsWindows-31Jになっているものだとばっかり思っていたが。CP0を認識できないからASCII-8BITとなっているようだ。
これだとWindows-31Jに決めウチで変換とかしないといけなくて、外国語Windowsに対応できない。
RubyではUTF16とかに変換して、あとはWin32APIで変換できるからそっちでやれということだろうか。
マジックコメントについても、なんでデフォルトがロケールじゃないんだろう、って思ってたけど、ロケールがCP0とか謎なものになってるならそれはそれで困るな。


なんだかわかったようなわからなかったような。
DXRubyのエンコード対応、きちんとできるかな・・・