みぃちゃんの頭の中はおもちゃ箱

略してみちゃばこ。泣いたり笑ったり

UTF-8をUTF-16に変換する

2011年10月18日 23時02分10秒 | IT・デジタル
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

最新の画像もっと見る

コメントを投稿

ブログ作成者から承認されるまでコメントは反映されません。