2015年2月18日水曜日

Unicode処理②::UTF16(Unicode)をUTF8に変換する。

Unicode : 文字コードの一種。

UTF16 , UTF8 : Unicodeをファイルに保存する場合の保存方法(バイナリ値)。


[例]=================================================

あ : Unicode値 : 0x3042

は、UTF16なら、
   big endian : 0x30 , 0x42
little endian : 0x42 , 0x30

↓変換……したい。

あ : UTF8なら、0xE3  81  82
(UTF8では、仕様上big endian も little endianも関係無い。)

====================================================

(14) UTF-16からUTF-8への文字コード変換
を参考にして変換してみる。(変換表はwikiのUTF-8にもある。)

変換表(実際にはUTF8は4Byte目までしか現在定義されていない。)
+------------------------+-------------------------------------------------------------------+
| UTF16                  | UTF8                                                              |
|                        | 1Byte目    2Byte目    3Byte目    4Byte目    5Byte目    6Byte目    |
+------------------------+-------------------------------------------------------------------+
| 0x00000000~0x0000007F | 0b0XXXXXXX ---------- ---------- ---------- ---------- ---------- |
| 0x00000080~0x000007FF | 0b110XXXXX 0b10XXXXXX ---------- ---------- ---------- ---------- |
| 0x00000800~0x0000FFFF | 0b1110XXXX 0b10XXXXXX 0b10XXXXXX ---------- ---------- ---------- |
| 0x00010000~0x001FFFFF | 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX ---------- ---------- |
| 0x00200000~0x03FFFFFF | 0b111110XX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX ---------- |
| 0x04000000~0x7FFFFFFF | 0b1111110X 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX |
+------------------------+-------------------------------------------------------------------+


0x3042は、0x00000800~0x0000FFFFの範囲にあるので、

UTF8の1バイト目       は、1110□□□□
UTF8の2バイト目以降は、1 0□□□□□□

このように、1~4Byteの可変長であるUTF8のバイト数が、
1バイト目の先頭から0までの間にある1の数として定義されているようです。

今回の場合は1バイト目に1が3つ立っているので、合計3バイトで「あ」の一文字を表します。

UTF8の1バイト目は、1110□□□□
UTF8の2バイト目は、10□□□□□□
UTF8の3バイト目は、10□□□□□□

この空いている□に0x3042を先頭ビットから代入していきます。
(ただし、下から詰めていき、最後に残った□は零で埋める事になります。)

UTF16  :  0x3042 == 0b  11  000001  000010

↓下から詰めていきます。

UTF8  :  1バイト目は、1 1 1 0 □□一一
UTF8  :  2バイト目は、1 0零零零零零一
UTF8  :  3バイト目は、1 0零零零零一零

↓残りの□を零で埋めます。

UTF8  :  1バイト目は、1 1 1 0 〇〇一一
UTF8  :  2バイト目は、1 0零零零零零一
UTF8  :  3バイト目は、1 0零零零零一零

↓整形

UTF8  :  1バイト目は、0b1110  0011  ==  0xE3
UTF8  :  2バイト目は、0b1000  0001  ==  0x81
UTF8  :  3バイト目は、0b1000  0010  ==  0x82

となり、無事UTF8に変換する事ができました。




ところで、UTF16で記録されたUnicodeの文字がサロゲートペアの場合は、
まず、バイナリを前回の逆手順でUnicode値に変換してからUTF8に変換する事になります。


UTF16で保存する時、バイナリの値は、「叱」の場合、
       big endian : 0xD8 42 , 0xDF 9F
    little endian : 0x42 D8 , 0x9F DF
となります。(エスケープシーケンスで書くと「\ud842\udf9f」)

これを、前回の逆手順で、「叱(Unicode値 : 0x20B9F)」に置き直してから、
今回の手順を適用する必要があります。



前回の逆手順の例

[例]=================================================

叱 : UTF16のエスケープシーケンスは、\uD842\uDF9F

バイナリでは、
       big endian : 0xD8 42 , 0xDF 9F
    little endian : 0x42 D8 , 0x9F DF

↓変換……したい。

叱 : Unicode値 : 0x20B9F

====================================================

0xD800~0xDBFFの範囲の文字(0b1101  10XX  XXXX  XXXX  (X : 任意))は上位サロゲートの文字で、
0xDC00~0xDFFFの範囲の文字(0b1101  11XX  XXXX  XXXX  (X : 任意))は下位サロゲートの文字と定義されています。

まずは符号を取り除きます。
上位サロゲート  :  0xD842 - 0xD800  ==  0x0042
下位サロゲート  :  0xDF9F - 0xDC00  ==  0x039F

0x0042  ==  0b0000  0100  0010
0x039F  ==  0b0011  1001  1111

(16~11Bitは符号ビットなので、)10Bit目までを、単純につなげると、

0b  0001000010   1110011111  ==  0x10B9F

最後に、サロゲートペアとする必要のある補助文字は、
0x10000~0x10FFFFの範囲の文字で定義されているので、
符号 0x10000 を加算する

0x10B9F  +  0x10000  == 0x20B9F



[例]=================================================

叱 : Unicode値 : 0x20B9F

↓変換……したい。

叱 : UTF8で表現すると、0xF0  A0  AE  9F
(UTF8では、仕様上big endian も little endianも関係無い。)

====================================================


0x20B9Fは、0x00010000~0x001FFFFFの範囲にあるので、
UTF8の1バイト目は、1 1 1 1 0  □□□
UTF8の2バイト目は、1 0□□□□□□
UTF8の3バイト目は、1 0□□□□□□
UTF8の4バイト目は、1 0□□□□□□

0x20B9F == 0b  100000  101110  011111
下の□から詰めていく

UTF8の1バイト目は、1 1 1 1 0  □□□
UTF8の2バイト目は、1 0一零零零零零
UTF8の3バイト目は、1 0一零一一一零
UTF8の4バイト目は、1 0零一一一一一

↓残りの□を零で埋めます。

UTF8の1バイト目は、1 1 1 1 0  〇〇〇
UTF8の2バイト目は、1 0一零零零零零
UTF8の3バイト目は、1 0一零一一一零
UTF8の4バイト目は、1 0零一一一一一

↓整形

UTF8の1バイト目は、0b1111  0000  ==  0xF0
UTF8の2バイト目は、0b1010  0000  ==  0xA0
UTF8の3バイト目は、0b1010  1110  ==  0xAE
UTF8の4バイト目は、0b1001  1111  ==  0x9F

となります。


注意 : Bloggerで「𠮟 : 0x20B9F」を表示しようとするとバグるので、「叱  :  0x53F1」で代用。
このままメモ帳にコピペしてバイナリエディタで開くと、「0x53F1」の方なので注意。

詳細は前回を参照の事。
(wikiのUnicodeの方はちゃんと0x20B9Fで書いてあるのがあるので、それをコピペするか、バイナリエディタで直打ちしてメモ帳で開いて見ると良い。TeraPadとかを使うとバグる。(TeraPadの問題だが、新しいバージョンで改善されているかは不明。))


実験用テキストファイル(Stirling.exeなどのバイナリエディタで観察する。)

前回 次回

0 件のコメント:

コメントを投稿