UTF-8からUTF-16に変換するプログラム。Cで書きました。
コードで使用しているchar16_t型は、現在はwchar_tと等価になっていますが、コンパイラに応じて適宜16ビット幅の整数型を使用してください。出力されるUTF-16文字のエンディアンは、処理系がビッグエンディアンならビッグエンディアンに、リトルエンディアンならリトルエンディアンになります。
Windowsマシンでコードを変換したので、stdinとstdoutをバイナリモードに切り替えています (For Windowsとコメントしたif文)。UNIX系のOSを使う場合は、このif文を削除してください。
U+10000からU+10FFFFの区間はサロゲートペアで出力するようにしましたが、未検証 (コラ)。これでいいはず。
ρ(∂_∂) ソースだよ☆
utf8to16.c:
/* Converts UTF-8 characters to UTF-16 */ #include <stdio.h> #include <io.h> #include <fcntl.h> /* Exit values */ #define SUCCESS 0 #define WARNING 128 #define FAILURE 255 typedef wchar_t char16_t; int main() { int retcode = SUCCESS; int c; fprintf( stderr, "utf8to16:¥n"); /* For Windows */ if( setmode( fileno( stdin), O_BINARY) < 0 || setmode( fileno( stdout), O_BINARY) < 0) { fprintf( stderr, "Cannot switch stdin/stdout to the binary mode.¥n"); return FAILURE; } while ((c = getchar()) != EOF) { unsigned long uc; size_t len; char16_t utf16[ 2]; while ((c & 0xC0) == 0x80) { fprintf( stderr, "Skipping unexpected UTF-8 tail '%02X'¥n", c); retcode = WARNING; c = getchar(); if (c == EOF) return retcode; } len = 0; if ((c & 0x80) == 0) { uc = c & 0x7F; len = 0; } else if ((c & 0xE0) == 0xC0) { uc = c & 0x1F; len = 1; } else if ((c & 0xF0) == 0xE0) { uc = c & 0x0F; len = 2; } else if ((c & 0xF8) == 0xF0) { uc = c & 0x07; len = 3; } else if ((c & 0xFC) == 0xF8) { uc = c & 0x03; len = 4; } else if ((c & 0xFE) == 0xFC) { uc = c & 0x01; len = 5; } while (len-- > 0 && ((c = getchar()) & 0xC0) == 0x80) uc = (uc << 6) | (c & 0x3F); utf16[ 0] = uc; len = 1; if (0x10000 <= uc && uc <= 0x10FFFF) { utf16[ 0] = 0xD800 | ((uc & 0x1FFC00) >> 10) - 0x40; utf16[ 1] = 0xDC00 | (uc & 0x3FF); len = 2; } fwrite( &utf16, sizeof( utf16[ 0]), len, stdout); } return retcode; }わざわざ自力でコードを書いたきっかけは、仕事で巨大なファイルが送られてきたことです。必要に応じてそのファイルを検索してデータを引っ張ってくる必要があります。ファイルの冒頭部分をのぞいてみると、コードはUTF-8でした。残念ながら、私が持っているgrepではUTF-8のファイルを検索できません。おまけにこのファイルは、データベースの内容を丸ごと出力したせいか、テキストファイルのくせに1GBもあります。 手元にあったフリーの文字コード変換ツールでシフトJISに変換しようとしましたが、ファイルが大きすぎて開けないとのエラーが出ました。いくら入力ファイルが大きいといっても、変換できない理由が理解できません。ファイルを先頭から少しずつ読み込み、順次変換して出力するだけなので、入力ファイル全体をメモリに読み込む必要はないはずですが。 しょーがないなぁ。 UTF-8からUTF-16に変換するフィルタを書くことにしました。UTF-16に落とせれば、あとはtypeコマンドでシフトJISに変換することもでき、融通がききます。 バイナリをコンパクトにしたかったので、C++ではなく、Cで書きました。Cなんて使うのは何年ぶりでしょうか。おかげでバイナリが54KBとコンパクトに収まりました! ヾ(≧∇≦)〃 わーい! 同じように困っている人がいるかも知れないので、簡単なエラー処理を組み込んでソースコードを公開した次第です。ご入用の方は、お好きなコンパイラでコンパイルしてご利用ください。 勢いでサロゲートペアにも手を出しましたが、サロゲートペアによる出力についてデバッグしていないのは、該当する文字が今回のデータになかったからです (手抜き)。誰かテストよろしくです☆ 参照資料: Wikipedia UTF-8 Wikipedia UTF-16