頼りなさげな wchar_t このエントリーをはてなブックマークに追加

otoco に限らず、 PC 上で動作するプログラムの多くは、テキストを処理することを目的の一部またはすべてとしています。 otoco の場合は特に、どこの誰とも分からない人が MML を書き、それを読み込んで XML やら SMF やらオーディオやら楽譜やらに変換することを目的としているので、どこの誰が MML を (あるいは XML を直接) 書いても問題なく処理できるよう、文字セットの扱いには丁重でなければなりません。

当初の方針として、 otoco では内部コードに Unicode を使用し、その物理型は wchar_t で扱うつもりでいました。この辺、C/C++ でのクロスプラットフォーム開発に慣れていないと陥りやすい罠であるように思うのですが… 現状の wchar_t ははっきり言ってクロスプラットフォーム開発には向いていないものといわざるを得ないようです。

とりあえず確認しているのは Windows の VC++ 2008 と Linux の gcc だけなのですが、それだけでも調べた限りで以下のような相違点がありました。

開発環境 文字セット 物理型
Windows + MS-VC++ 2008 UTF-16LE 符号無し 16bits 整数 (unsigned short)
Linux + gcc UCS4 符号無し 32bits 整数 (uint32_t)

まず文字セットですが、 UTF-16LE とはリトルエンディアンの UTF-16 エンコードのことで、 Unicode を表現するためのファイル形式です。ファイル形式であるということは、すなわちファイルに保存する方法を定めた形式であるということです。それに対して、 UCS4 はあくまで Unicode そのものであり、内部データ形式として扱えるものです。

具体的に何が違うのかというと、 UTF-16 の場合は配列内の数値 1つが必ず 1文字を表現するものであることを保証しません。実際、UTF-16 ではサロゲートペアを気にする必要があり、この処理を誤ると文字境界に破綻を来して文字化けの原因を作ってしまうことになります。これに対し、 UCS4 の場合は単に 31bits 以下の文字セットであり、それより拡張されないことが保証されています (万一拡張された場合は新たに UCS8 が規定されて包括されるのでしょうが、現実的にはあり得ないでしょう)。

私は元より Windows 畑の人なので、 wchar_t を使う場合でもサロゲートペアをどうにかすることを前提に考えていましたから、 GNU/Linux でのあり方はむしろ理想的とも思うのですが、反面内部的な処理に過ぎない部分でプラットフォーム依存を気にしながら実装しなければならないというのはあまり好ましいことではなく、そう考えると wchar_t という型は意味論的には破綻しているといわざるを得ないように思います。さらに BSD 系の UNIX 環境では wchar_t が扱う文字セットは環境のロケールに依存するなどという情報もあり… とてもじゃないですがそんなの考慮しきれるわけがありません ((((/;^^)/ 。

というわけで、内部コードは wchar_t のような型名で定義するのではなく、より具体的に文字セットで定義した方が良さそうだなぁという結論に至りました。候補は以下の 2通りです。

  • UCS4 を内部コードとし、物理型は符号無し 32bits 整数を適当な型名に typedef して用いる。
  • UTF-8 を内部コードとし、物理型は char を用いる。

前者のメリットは何と言っても多言語処理の確実性が高く、文字境界も気にする必要がないことです。例えば、配列の中の n個目の値は、確実に文字列の中の n個目の文字であることが保証されます。反面、 STL や Boost を用いた文字列処理においては、あらかじめ typedef された便利な型名を用いることができず、プログラム側で内部コード用に typedef したものをたくさん用意しておく必要が生じるでしょう。また、何より文字列リテラルが使えなくなるので、正規表現のハードコーディングには工夫を強いられることになります。

後者のメリットは STL の string や Boost.Regex に定義されている typedef がそのまま利用できること、そして何よりハードコーディングした文字列リテラルがそのまま利用できることです。正規表現の記述もこちらの方がよっぽどすっきりするでしょう。また、 XML の入出力を UTF-8 に限定して良いのであれば、その辺の実装も楽になるかも知れません。文字境界については注意する必要がありますが、例えば n文字目の検出は他のエンコーディングに比べれば容易であるのも UTF-8 の特徴でもあります (もちろん、UCS4 を用いる場合に比べれば、実装は複雑になりますが…)。

ちなみに、文字セットの変換にはやっぱり iconv を使うことになりそうです。 Windows 側はまだ試していないのですが… とりあえず近日中に iconv を用いた簡単なプロトタイプを書いて、上記の件も含めて検討してみる予定です…。

2009 年 7 月 25 日 by 村山 俊之

タグ: , , , , , ,

コメント / トラックバック 3 件

  1. はらぺこ日誌» ブログアーカイブ » libiconv で文字セット自動認識 より:

    [...] 以前、wchar_t はどうにも使い物にならないからどうしよう、といった記事を書いたのですが、その続きのお話です。 [...]

  2. はらぺこ日誌» ブログアーカイブ » char32_t だと regex が使えない より:

    [...] char と wchar_t しか想定していなかったんですね (ん? でも GCC の wchar_t は uint32_t [...]

  3. はらぺこ日誌» ブログアーカイブ » C++11 で Unicode プログラミングのススメ より:

    [...] wchar_t については大分昔に見捨てているんですが (^_^; 、VC++2010 だと 32bits [...]

コメントをどうぞ