Entry

プログラミングメモ - iconv を少し読む

2009年11月25日

私家版の文字コード変換ライブラリを作ってるぜ,の続き。JIS X 0213:2004 には,U+30AB+309A("カ"に半濁点がついた文字)なんて文字が定義されていて,UCS-2 や UTF-16 ではこれを2つの2バイト文字として表現します。で,私家版のライブラリでは,内部の文字コードの軸を Unicode にしようと思っているんですけれど,JIS X 0213:2004 から Unicode(または UCS)の変換するテーブルをどうやって構成したらいいか,ちと迷っています。手元のプログラムでは,とりあえず富豪プログラミングで,1文字を4バイトで表してもいいことにしているんですけれど,そもそもこの文字は,UCS-4 や UTF-32 の合成済み文字として表せないんだろうか。

ま,これは調べりゃ分かることだから,ちと横に措いておきます。多分できるんだろう。一方で,それよりも気になるのは,他のモジュールの実装です。参考までに iconv のライブラリ側(libiconv)を少し読んでみたんですけれど,どうも UTF-16 で表現しているみたい。やっぱりこうするしかないのかなー。

具体的には,こんな風に実装している。まず,lib/jisx0213.h。配列 jisx0213_to_ucs_main は,JIS X 0213:2004 のコードを UCS にマッピングするテーブルです。iconv のバージョンは 1.9.1。/** **/ のコメント部分は aian がつけたコメントです(以下同じ)。

static const unsigned short jisx0213_to_ucs_main[120 * 94] = {
    /** (snip) **/
    /* 0x12521..0x1257E */
    0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8,
    0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0,
    0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8,
    0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0,
    0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x10c6, 0x10c7, 0x10c8,
    0x10c9, 0x10ca, 0x10cb, 0x10cc, 0x10cd, 0x10ce, 0x10cf, 0x10d0,
    0x10d1, 0x10d2, 0x10d3, 0x10d4, 0x10d5, 0x10d6, 0x10d7, 0x10d8,
    0x10d9, 0x10da, 0x10db, 0x10dc, 0x10dd, 0x10de, 0x10df, 0x10e0,
    0x10e1, 0x10e2, 0x10e3, 0x10e4, 0x10e5, 0x10e6, 0x10e7, 0x10e8,
    0x10e9, 0x10ea, 0x10eb, 0x10ec, 0x10ed, 0x10ee, 0x10ef, 0x10f0,
    0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x0006, 0x0007,
    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d,
    /** (snip) **/
};

強調した部分が極端に数値が小さくて気になりますよね。これじゃ ASCII に引っかかっちゃいます。ご推察の通り,この場所が先の合成文字に対応するエントリになっています。もちろん,この数値は UCS の文字コードそのものではなくて,別の意味がある。どゆことかというと,他のテーブルへのインデックスになっているんですね。

じゃ,このインデックスはどのテーブルで使われているのか。これは jisx0213_to_ucs_combining という,いかにもな名前のテーブルにマッピングするためのインデックスになっています。

static const unsigned short jisx0213_to_ucs_combining[][2] = {
    { 0x304b, 0x309a }, /** 001 **/
    { 0x304d, 0x309a }, /** 002 **/
    { 0x304f, 0x309a }, /** 003 **/
    { 0x3051, 0x309a }, /** 004 **/
    { 0x3053, 0x309a }, /** 005 **/
    { 0x30ab, 0x309a }, /** 006 **/
    { 0x30ad, 0x309a }, /** 007 **/
    { 0x30af, 0x309a }, /** 008 **/
    { 0x30b1, 0x309a }, /** 009 **/
    { 0x30b3, 0x309a }, /** 010 **/
    { 0x30bb, 0x309a }, /** 011 **/
    { 0x30c4, 0x309a }, /** 012 **/
    { 0x30c8, 0x309a }, /** 013 **/
    { 0x31f7, 0x309a }, /** 014 **/
    { 0x00e6, 0x0300 }, /** 015 **/
    { 0x0254, 0x0300 }, /** 016 **/
    { 0x0254, 0x0301 }, /** 017 **/
    { 0x028c, 0x0300 }, /** 018 **/
    { 0x028c, 0x0301 }, /** 019 **/
    { 0x0259, 0x0300 }, /** 020 **/
    { 0x0259, 0x0301 }, /** 021 **/
    { 0x025a, 0x0300 }, /** 022 **/
    { 0x025a, 0x0301 }, /** 023 **/
    { 0x02e9, 0x02e5 }, /** 024 **/
    { 0x02e5, 0x02e9 }, /** 025 **/
};

2バイトのワイド文字を2つ使う文字の場合は,この2次元配列で定義されています。jisx0213_to_ucs_main で 0x0006 がある場所は U+30AB+309A に対応する文字で,jisx0213_to_ucs_combining の 6 番目の要素にこの文字が登録されています。ちなみに,0x0000 にはまた別の特別な意味があるので,マッピングするインデックスは 0x0000 からではなく 0x0001 から始まります。

一応,実際にこのテーブルを使っているとこ(lib/iso2022_jp3.h)。

if (wc < 0x80) {
    /* It's a combining character. */
    ucs4_t wc1 = jisx0213_to_ucs_combining[wc - 1][0];
    ucs4_t wc2 = jisx0213_to_ucs_combining[wc - 1][1];
    /* We cannot output two Unicode characters at once. So,
       output the first character and buffer the second one. */
    *pwc = wc1;
    conv->istate = (wc2 << 3) | state;
} else {
    *pwc = wc;
    conv->istate = state;
}

0x80 未満,つまり ASCII コードとかぶっている部分を,合成文字用のテーブルへマッピングするインデックスとして使っているようです。これはこれでいいんだろうけれど,1文字を32bitとして扱うなら,ちと迂遠な感じもする。どうなんだろう。

細かいことを考え出すときりがないんですけれど,せっかく Unicode/UCS を使うのに多バイト文字と同じように処理しなくちゃいけないのは,ちと困ったちゃんです(つか,面倒くさい)。どうしたもんだろう。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN