RubyCocoa 文字列のエンコーディング (続き)
先週の続き。
試しながら考えたや課題など。
st_tableによる変換表
Rubyのencoding indexとCFStringEncodingはどちもintなので特に問題なく。
これは「同じエンコーディングは同じ符号化(バイト列)」であることを前提にしてるので、RubyのUTF-8-MacをUTF-8にしてからCFStringに渡す、ということはできない。これを実現したい場合、入力エンコーディングに対して
- 変換の要否、その方法
- 変換先のエンコーディング
といった情報をHashなどで持たせるような変換表にする必要があるだろう。このようにするべきかどうかは結論がでてない。
また、Rubyのエンコーディングは拡張ライブラリ(?)になっていて必要なときに読み込みするようになっているので、最低限の変換表(ASCII-8BIT, UTF-8)だけ初期登録しておいて、あとは実行時に追加していくのが良いと思う。(そうしないとindexがわかっていても、エンコーディング本体がロードされてなくて妙な動作をしたりする。)ただこの「実行時に追加」をどうするのかが考えどころ。
名前(char *)の変換表を内部的に持つあたりかなあ。ということで次へ。
名前によるエンコーディング変換
RubyもCFStringも「エンコーディングの名前」を持っているので、これを使えばよいのでは?と試してみた。
CFStringEncodingで使えそうなのは、
- IANA charset名 (CFStringConvertEncodingToIANACharSetName)
- Windowsコードページ (CFStringConvertEncodingToWindowsCodepage)
あたり。IANAでない名前を取ることはできる(CFStringGetNameOfEncoding)けれど、名前から引くことはできないので除外。
RubyのEncodingで使えそうなのは
- 名前 (Encoding#name)
あたり。RubyのEncodingは名前を複数持てるんだけど、Rubyのメソッドを通さないと拡張ライブラリからはひとつめ名称にしかアクセスできないっぽい。
で、とりあえずIANA charset名と名前で引けたものは自動的に変換テーブルに登録していくってのをやってみたんだけどうまくいかない。UTF-16がどうもうまくない。
NSString|CFStringは、Rubyの文字列とちがってある文字列を特定のエンコーディングとして認識しているのではなく、Cのバイト列との相互変換のときにエンコーディングを都度指定するようになっている。NSStringのインスタンスに対しては
- fastestEncoding: バイト列にするときに時間がかからないもの。
- smallestEncoding: バイト列にしたときそのサイズが小さいもの。
- canBeConvertedToEncoding: 情報のロスなしに指定のエンコーディングに変換可能か。
などを問い合わせることができる。
この「エンコーディングそのものを持ってない。目的に応じて使えるエンコーディングを問い合わせることができる」てのがくせ者で、UTF-8で生成した文字列が、fastestEncodingでは(BEでもLEでもない)UTF-16を返すということがある。するとRuby側からUTF-8で渡した文字列がObjCから帰ってくるときにUTF-16になってしまう。これはNSString|CFStringの仕様的にどうにもならんのだけど気持ち悪い。
きょうびUTF-8がほとんどだと思うので、CFString(UTF-16)→Ruby Stringの場合はUTF-8に変換しちゃうほうがよいかもしれない。