UTF-8をUTF-32に変換するコード
Posted on 2014年9月8日(月) 23:53
UTF-8って素晴らしいですよね。
unicodeは元々、世界中の全ての文字を16bitで表現するという英語圏以外の人からすればあまりに愚かな試みからスタートしましたが、もちろん破綻。
16bitを使いながら、サロゲートペアを使って16bit以上のコードも表現するようになりました。
結局サロゲートペアが必要な時点でUTF-16は欠点ばかりのものとなってしまいました。
最近よく使われるのはUTF-8です。ASCIIコードと互換性が保ちやすく大変便利ですね。
UTF-8は結局のところUTF-32をシリアライズしただけのものなんですが、へぇよく考えてるなぁという感じなのです。
任意のバイトストリームから混同せずに文字列を取り出せます。
ただ欠点もあって、色々処理をするときに文字ごとに長さが違うので厄介です。
そこでUTF-8から32bit表現(UTF-32)に変換するコードを書いてみました。
プログラムの内部的にはUTF-32を使うと捗りますので。
このようなUTF-8の欠点はサロゲートペアがある時点でUTF-16でも同様なんですが、サロゲートペアはあまり出てこないので無視してしまってる駄目な処理系もあるような気がします。
なお、セキュリティの観点からはUTF-8の冗長表現が問題になることもあります。
本来ASCIIコードである文字でももっと大きなバイス数で表現することが出来てしまうので不正文字チェックを抜けてしまうということです。
例えば0xC0 0xAF(11000000 10101111)は、2バイトのUTF-8として扱うと[101111]の部分がデータになりますので、0x2F(バックスラッシュ)と等価になります。
つまりより小さいバイトのコードは大きなバイト数でも表現出来てしまうため、単純チェックだとすり抜けるということです。
仕様ではこのようなバイト列は不正なUTF-8バイト列としなければなりません。
また5バイトと6バイトのUTF-8も現在ではUnicodeとしては不正とされていますがISO/IEC 10646では存在しています。
以下のコードではそのような不正なUTF-8もそのまま変換します。
unsigned long utf8ToUtf32(unsigned char *input, int *bytesInSequence) { unsigned char c1, c2, c3, c4, c5, c6; *bytesInSequence = 1; if (!input) { return 0; } //0xxxxxxx (ASCII) 7bit c1 = input[0]; if ((c1 & 0x80) == 0x00) { return c1; } //10xxxxxx high-order byte if ((c1 & 0xc0) == 0x80) { return 0; } //0xFE or 0xFF BOM (not utf-8) if (c1 == 0xfe || c1 == 0xFF ) { return 0; } //110AAAAA 10BBBBBB 5+6bit=11bit c2 = input[1]; if (((c1 & 0xe0) == 0xc0) && ((c2 & 0xc0) == 0x80)) { *bytesInSequence = 2; return ((c1 & 0x1f) << 6) | (c2 & 0x3f); } //1110AAAA 10BBBBBB 10CCCCCC 4+6*2bit=16bit c3 = input[2]; if (((c1 & 0xf0) == 0xe0) && ((c2 & 0xc0) == 0x80) && ((c3 & 0xc0) == 0x80)) { *bytesInSequence = 3; return ((c1 & 0x0f) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f); } //1111 0AAA 10BBBBBB 10CCCCCC 10DDDDDD 3+6*3bit=21bit c4 = input[3]; if (((c1 & 0xf8) == 0xf0) && ((c2 & 0xc0) == 0x80) && ((c3 & 0xc0) == 0x80) && ((c4 & 0xc0) == 0x80)) { *bytesInSequence = 4; return ((c1 & 0x07) << 18) | ((c2 & 0x3f) << 12) | ((c3 & 0x3f) << 6) | (c4 & 0x3f); } //1111 00AA 10BBBBBB 10CCCCCC 10DDDDDD 10EEEEEE 2+6*4bit=26bit c5 = input[4]; if (((c1 & 0xfc) == 0xf0) && ((c2 & 0xc0) == 0x80) && ((c3 & 0xc0) == 0x80) && ((c4 & 0xc0) == 0x80) && ((c5 & 0xc0) == 0x80)) { *bytesInSequence = 4; return ((c1 & 0x03) << 24) | ((c2 & 0x3f) << 18) | ((c3 & 0x3f) << 12) | ((c4 & 0x3f) << 6) | (c5 & 0x3f); } //1111 000A 10BBBBBB 10CCCCCC 10DDDDDD 10EEEEEE 10FFFFFF 1+6*5bit=31bit c6 = input[5]; if (((c1 & 0xfe) == 0xf0) && ((c2 & 0xc0) == 0x80) && ((c3 & 0xc0) == 0x80) && ((c4 & 0xc0) == 0x80) && ((c5 & 0xc0) == 0x80) && ((c6 & 0xc0) == 0x80)) { *bytesInSequence = 4; return ((c1 & 0x01) << 30) | ((c2 & 0x3f) << 24) | ((c3 & 0x3f) << 18) | ((c4 & 0x3f) << 12) | ((c5 & 0x3f) << 6) | (c6 & 0x3f); } return 0; }