mbstring/libmbflへのgb18030サポート追加

GB18030 のサポート

大国中国のオフィシャルな文字コードということで以前から気にはなっていたのですが、夏休みということでトライしてみました。
なにしろ、Unicodeのコードポイントを超える150万字をサポートする世界最大と言っても良いかもしれない文字コードです。以前、mbstring/libmbfl用にgb18030用パッチを作成された方がいらっしゃいましたが、その時のパッチは30Mバイトくらいになっており、さすがに取り込むのは困難だったような記憶があります。

Unicodeの16面をすべて含むというスケールの大きさはさておいて、実態は、GB2312、GBK、CP936といった既存の文字コードを拡張し、Unicodeとの相互マッピングを行えるようにしたものです。Unicodeの部分は算術変換で記述できるようです。

GB18030は、1バイト領域+2バイト+4バイト(Unicode)という作りで、構造的には、他のページにも書いてありますが、UTF-8の中国語版と言えるかもしれません。ただし、UTF-8と異なり、1バイト目で2,4バイトが区別できる作りになっていません。2,4バイト文字を区別するには、2バイト目を見る必要があります。これは、GBKとの互換性を保つため、1バイト目を変えられなかったからだと思いますが、結果的に文字列長を調べることを困難にしています。

libmbflへの実装ですが、ucmのようなコードページをそのまま実装してlibmbflを巨大化させることには問題があるため、すでにlibmbflでサポートされているCP936をベースに算術変換をできるだけ使って、追加データを最小化することにしました。

コードの実装

参考にしたのは、perl の Encode::HanExtra、libiconv、ICU (GB 18030 )とかです。よくわからないのは、Euro記号をCP936互換で GB+80=U+20ac とするのか、GB18030マッピングを採用するのか、0xFF はどうするのか等です。UCMファイルは、Encode::HanExtra、ICUで提供されていますが、これらは微妙に異なるようです。

また、U+1E3F,U+E7C7 のマッピングICUとlibiconvでは事なるようです。現在は、libiconvのマッピングを使用しています。

各実装を見てみると、Encode::HanExtraは、ucmを読み込んでマッピングを行っているシンプルな実装のように見えます(間違えていたらごめんなさい)。ICUは、算術符号をUnicode に対応する4バイト符号のところに使っているようです。libiconvは最もアグレッシブに算術変換を駆使しています。

ということで、libiconvを参考に実装コードを作成してみました。Unicode BMPをサポートするところは、基本的に連続しているところが多いのですが、この部分はオフセットをテーブルで読みながら算術変換することになります。UnicodeBMP以外は算術変換でできます。その他の部分は、既存のCP936の変換テーブルを拡張して対応し、残ったところは、テーブルを作成します。これで、マッピングテーブルをあまり増やさずにサポートできそうということになりました。

現状は、Encode::HanExtra付属のgb18030.ucmを使って確認していますが、よく仕様がわからない0xFF (gb18030.ucmに2種類定義されている!)以外は変換できています。

PHPへの適用

PHP 5.4に含めるかどうかはまだ決めていません。このページを中国の方が見ているとは思えませんが。。。

PHP 5.4, PHP 5.5 に適用しましたので、svn.php.netから取得することができます。

使用法

現時点で、github のlibmbflオフィシャルレポジトリからソースコードを取得できます。PHPソースコードへの組み込みもconfig.m4 に mbfilter_gb18030.c を追加する程度でうまくいいくはずです。
(手元のテストはPHPに組み込んだ状態で行っています。)

PHP 5.4.0 beta 1 が公開された時点でテストできます。