(2019.10)
ICU を使った文字コード変換について。ある文字コードのテキストを別の文字コードへ変換する際、間に、必ず Unicode を経由する。
ICUでは, Unicode code point は UChar32
型, UTF-16 code unit は UChar
型で表す。文字列は UnicodeString
クラスのオブジェクト (内部 UTF-16 固定) になる。
データ列がシフトJIS、日本語EUCなどUnicode以外のときは、読込み時に, 何らかの方法で変換してやる必要がある。
UNIXであれば iconv()
が使えるので、それを使ってもよい。変換表をいくつもあれこれ使うと、思わぬところで文字化けしたりする恐れがある。iconv()
を使わないのなら、ICUの変換器を使う。UConverter
クラスが文字コードの変換器。
次のソースコードは、日本語EUCからUnicodeへ変換したうえで、UnicodeString
オブジェクトを作る。
まずは、必要なヘッダを include
する。
3| #undef USE_ICONV 4| 5| #include <stdio.h> 6| #include <string.h> 7| #include <assert.h> 8| #ifdef USE_ICONV 9| #include <iconv.h> 10| #else 11| #include <unicode/ucnv.h> 12| #endif 13| #include <unicode/unistr.h> 14| #include <unicode/uchar.h> 15| #include <unicode/schriter.h>
iconvを使った変換は, iconv_open()
で文字コードを指定し、iconv()
で変換する。変換先は UTF-8 or UTF-16 にすること。次の例は, 変換後の大きさが 1,000 バイトに収まるという仮定を置いている (手抜き) ので、実用にするときは修正が必要。
変換したバイト列と文字コード名をUnicodeString
に与えて文字列オブジェクトを生成する。
17| #ifdef USE_ICONV 18| static const char* UTF8_CES = "UTF-8"; 19| static const char* EUCJP_CES = "eucJP-open"; 20| #endif 21| 22| int main() 23| { 24| const char* euc = "ABC日本語のテキスト\\/¥/\"; 25| #ifdef USE_ICONV 26| // エンコーディングの変換:iconvを使う場合 27| iconv_t cd = iconv_open(UTF8_CES, EUCJP_CES); 28| size_t left = strlen(euc); 29| char buf[1000]; 30| char* p = buf; 31| size_t bufleft = 1000; 32| printf("euc = %x, p = %x\n", euc, p); 33| size_t r = iconv(cd, const_cast<char**>(&euc), &left, &p, &bufleft); 34| printf("euc = %x, p = %x\n", euc, p); 35| printf("r = %d, left = %d, bufleft = %d\n", r, left, bufleft); 36| *p = '\0'; 37| iconv_close(cd); 38| UnicodeString str(buf, UTF8_CES);
今度はICUのコンバータを使ってみる。ucnv_open()
でUConverter
オブジェクトを生成し、それと変換元のバイト列をUnicodeString
に与えるだけでいい。
ucnv_open()
に渡す文字コード名だが、日本語EUCの場合は、mappings/convrtrs.txtを見ると、次の3つがある。ibm-954
は NEC特殊文字が全滅。ICU では EUC-JP
がまともな変換表。
ucnv_open()に渡す文字コード名 | コメント |
---|---|
ibm-33722_VPUA
| ibm-5050. JIS X 0208記号も足らない。不可。 |
ibm-33722_P120-1999
| '\' => yen sign. NG |
ibm-954 | NEC特殊文字とその他の記号が収録されていない。不可。 |
EUC-JP | これが一番まとも。 |
その他、aliasなどは、次のページで見れる;
39| #else 49| UErrorCode error = U_ZERO_ERROR; 50| UConverter* cnv = ucnv_open("ibm-954", &error); 51| assert(U_SUCCESS(error)); 52| UnicodeString str(euc, strlen(euc), cnv, error); 53| assert(U_SUCCESS(error)); 54| #endif
ある文字コードのバイト列を、別の文字コードに変換する。レガシーな文字コードの場合だけでなく、UTF-8などの場合も、このようにすればいい。
メモリ上にバイト列が納まる場合は、ucnv_convert()
が一番簡単。
メモリに載りきらない場合や、ストリームを読込みながら変換したい場合は、やや複雑になる。
1文字の途中で入力のバイト列がいったん切れることもあるし、エスケープシーケンスで文字集合を切り替えるようなものだと、変換器が状態を持つ。
まずは、呼び出す部分。
本題の変換する部分。
こういう用途では ucnv_convertEx()
も考えられるが、使い方があまりに複雑なため、よくない。
ucnv_toUnicode()
と ucnv_fromUnicode()
を組み合わせるのがよい。中間表現として pivot バッファを用意し、いったん Unicode に変換する。
ucnv_open()
で変換器を生成し、ループを回している間、これを使いまわす。ucnv_close()
を忘れずに。
手抜きだが、こんな感じで上手くいく。
(2024.7) JIS X 0213:2004 に収録されている文字のうち一部は, Unicode では 2 code points になる。JIS X 0213コード表(1)1面1-23区 JIS 1面の文字のみ。
EUC-JP は,
ELEM_SIZE
の要素 (code unit) 数は, source_buf
のバイト数と同じで差し支えない。