やっぱりwcharは使いづらいかも・・・。

少し間があいてしまいました。 前回、次はC++じゃない話を・・・と書きましたが今回もやっぱりC++の話です。

C言語C++でも、日本語のようなマルチバイト文字列を扱おうという話になった時に、まず選択肢としてあがるのがwchar型でしょう。 wchar型で扱えば、例えば「MHP3で狩り解禁」といった日本語とASCIIが入り交じった文字でも、ちゃんと文字単位で処理することができるようになります。

wcslen(L"MHP3で狩り解禁")  => 9

ですが、このwchar型は、いろんなサイトで言われている通り非常に厄介な代物で、内部の文字エンコーディング形式が処理系依存となっています。

UTF-16は、基本的には1文字を16 bit, つまり2バイトで表現するエンコーディング形式です。 UTF-32は、1文字を32 bit, つまり4バイトで表現するエンコーディング形式です。

UTF-16で「基本的には」と書いているのは、実は世界中の文字集合を表現しようとすると2バイトでは足りず、一部の文字については4バイトで表現されているからです。この4バイトで表現する手法を「サロゲートペア」と呼びます。 日本語もサロゲートペアと無関係ではなく、2004年に制定された文字集合「JIS X0213」に含まれる一部の文字が該当してしまいます。例えば「草なぎ剛」の「なぎ」が代表的な文字です。

移植性も考えると扱いづらくて仕方のないwcharですが、最近以下のようにiconvを使うことで、任意の文字コードからwcharの内部エンコーディング形式に変換できることを知り、実は結構使えるんじゃないか?と興味が湧きました。

 cd =  iconv_open("wchar_t", original_code);

もしwcharが十分に使えるのであればwstringやwostreamといったクラスが利用可能となり、C++で日本語文字列を扱うのが格段に楽になります。 そこでサロゲートペアを利用するような文字であっても正しく処理することができるかを検証してみました。

まずはUbuntu Linux 64bit gcc 4.4.5の環境から。 LinuxではUTF-32が利用されるので問題なく処理することが可能です。 試しに以下のコードで「𠀋」という文字のwstringの文字数を数えてみると「1」文字であると出力されました。 (コード例は細かいところは結構適当ですので、参考にはしないでください) [gist id=754224 bump=1]

次はWindows 7 64bit, Visual C++ 2008ではどうなるかを確認してみます。 「あ」と「𠀋」の2文字の文字数をそれぞれ数えてみました。 [gist id=754247 bump=2]

「あ」という文字は文字数「1」と出力されますが、「𠀋」という文字は文字数「2」が出力されます。 {追記} 最初のコードは間違っていたため「6」とおかしな数字を出力していました。現在は修正され「2」という数字が出力されるようになりました。 ですが、本来であればサロゲートペアであってもLinuxと同じく「1」が出力して欲しかったのですが、そうでなかったのは残念です。

この現象が起こるのは、あまり使わない特殊な文字が入力された時だけじゃないかという話はあるかと思いますが、少なくとも、こういう問題がある、ということを認識しておかなければ、原因不明のバグに悩まされることになります。