Gauche:charconv
iconvの問題
文字コード変換モジュール gauche.charconv はiconv()を利用しているのだが、 Schemeでのコード変換ライブラリ Jfilter 等を開発した 犬飼さんから、 「Shift JISのファイルが変換できずにエラーになる」という報告が寄せられた。 犬飼さんの協力で調査したところ、GNU libiconv及びglibcのiconv (以降、gnu-iconvと総称)には次のような問題があることがわかった。 ちなみに、gnu-iconvはUCS4を介してコード変換を行っている。
- sjisの0x5cをgnu-iconvは U00A5 にマップする。GNU libiconv-1.7では、 このコードをeuc-jpに変換する部分がうまく動作せずエラーとなる。 (CVS treeでは修正されている。また、glibc-2.2.4でもこれは動作する)。 U+00A5へのマップはftp://www.unicode.org/ の表に規定されているらしい ( http://www.autumn.org/etc/unidif.html )のだが、こうしてしまうと sjisで書いたプログラムソースコードをunicode変換したら困ったことにならんだろうか。
- jisx0208の未定義領域(Row 9からRow 15)にマップされるsjisコードに出会うと gnu-iconvはエラーを返すが、この領域はベンダ独自の拡張文字で使われている ことがある。
他にも問題はあるかもしれない。
- sjisの0x5c問題は、もしかするとShift_JISでなくてCP932を指定したら 良かったのかもしれない。未確認。
- glibc-2.3のiconvはjisx0213をサポートし、regexpもマルチバイト拡張 されているそうな。広まってきたら、Gauche内の実装とglibcの実装の どちらを使うかをconfigureで選べるようにすると良いかもしれない。 glibcの無い環境のことも考えると、Gauche内での実装を削っちゃうわけには 行かないだろうけど。Shiro (2002/10/05 16:50:29 PDT)
解決策
2通りの対応が考えられる。たぶん両方とも実装すべきだろう。
- charconv内で、illegal byte sequenceエラーに出会った時のハンドラを
定義できるようにしておく。例えば以下のような処理が選べるようにするとか。
- エラーを投げる
- 未定義文字を代替文字にマップする
- 未定義文字を無視する
- traditionalな日本語文字コード間の変換に関してはiconvを頼らずに 独自実装する
未定義領域へのマップの問題は前者の実装で解決できるだろう。 しかし、0x5c->U00A5の問題は残るし、システム提供のiconvが日本語コードを サポートしてない場合も考えると、後者も対応しておいたほうがよさそうだ。
結局、JIS X 0213:2000及びそのEUC-JPとSJISへのエンコーディングを基本とした エンコーディング変換ルーチンを自前で持つことにした。→ 0.5.6で実装
メモ
ISO2022-JPみたいに、statefulなエンコーディングで最後に初期ステートに 戻すことが要求されている場合、最後にiconv(3) をinbuf=NULLで呼び出すことで それを行う。ただiconv_closeしただけではだめ。
glibc-2.2.4のiconv(2)では、境界例のテストコードを作ることもままならない。 (JIS X 0201 KANAを含むEUC_JPデータを食わせたらエラーになった)。 nkfを使ってSJISとISO2022JPは生成出来たが、UTF-8はいかんともしがたい。 Mule-UCSを入れようと決意。xemacsを21.4.6にまずアップデートして、 Mule-UCS-0.84-1.src.rpmからrebuildを試みたんだが、 なんかelのコンパイル中にOSごと落ちるのだ。こんなことははじめてだ。 メモリのフットプリントがでかそうなので、vmのどっかの不具合にひっかかったのか? (何せいまどきメインメモリ192MBのマシンである)。 メモリ394MB積んだノートPCの方でrebuildしてみたら無事作れた。(2002/06/09 00:04:57 PDT)
iconv(3)のインタフェースって、from_codeの複数文字が to_codeのひとつの文字に対応する場合に使えないんじゃなかろうか。
from_codeの文字をFn, to_codeの文字をTnと表記することにして、 「Fa単独だとTaへマップされるが、[Fa Fb] というシーケンスに限り Tb へとマップされる」 というルールがあるとする。
input buffer内で1文字先読みすれば良さそうだが、input bufferの最後にFaが 来た時、それがたまたまバッファがそこで切れているのか、ストリームがそこで 終っているのかiconvにはわからない。
まあ、一つの方法として、Faをエスケープシーケンスのように扱い、 その後にFbが来たら Tbを出力、それ以外(Fc)が来たら [Ta Tc]と2文字出す、 というのは考えられるな。ストリームの最後がFaだった場合は、最後に iconvがinbuf==NULLで呼ばれた際にTaを出力、と。 つうかこれしかやりようが無い。
資料集
- Shift_JISのエイリアスの変更について : Shift-JIS -> UnicodeとMicrosoft CP932 -> Unicodeのマッピングの違い
- 日本語 EUC シフト JIS 間コード変換仕様とコード系実態調査 : OSFのWGの資料。
- Shift-JISとUnicode間の変換問題 : Microsoftの資料。拡張文字のn-to-1mapping
- Shift-JISのコーディング : 機種依存文字の表など
- 新JIS漢字実践情報 : JIS X 0213に関する情報
- JISX 0213の代表的な符号化方式
- JIS-UCS変換表
- UnicodeとCJKエンコーディングとの相互運用の問題 : ここ-はすごく分かりやすく充実している。
- eucJP-openとUCSとのコード変換