Q. UTF って何?
Unicode (または UCS) Transformation Format の略語です。今のところ、UTF-1, UTF-2, UTF-5, UTF-6, UTF-7, UTF-8, UTF-9, UTF-16, UTF-17, UTF-18, UTF-32 があります。しかし、実際使用されているのは、UTF-8, UTF-16, UTF-32 です。
Q. UCS って何?
Universal Character Set の略語です。ISO 10646 の文字集合をあらわしています。
Q. BE, LE って何?
UTF-16BE, UTF-16LE, UTF-32BE, UTF-32LE のように UTF-XX の後に BE または LE が付くことがあります。これは、その文字コードを表現しているバイナリのエンディアンを明示しています。
BE (Big Endian)
LE (Little Endian)
このように、エンディアンを明示する方式のほかに BOM をファイル先頭に挿入して、暗黙的にエンディアンを識別させる方法もあります。
Q. BOM って何?
Byte Order Mark の略で、文字コードのエンディアンを判定するために用いられます。コードポイントは U+0000FEFF です。これをファイルの先頭などに挿入することにより自動的にエンディアンを判定できます。ちなみに、U+0000FEFF はゼロ幅空白 (Zero Width Non-breaking Space) をあらわしています。
|
先頭からの数バイト | UTF 名 |
|
00,00,FE,FF | UTF-32BE |
FF,FE,00,00 | UTF-32LE |
FE,FF | UTF-16BE |
FF,FE | UTF-16LE |
EF,BB,BF | UTF-8 |
|
Q. 各 UTF 間では自由に変換できるの?
それぞれの変換方式が想定している制限を考慮すると次のような順番で変換する必要があります。
UTF-1, UTF-8 (UTF-2), UTF-5, UTF-9, UTF-17, UTF-18 ⇔ UTF-32 ⇔ UTF-16 ⇔ UTF-6, UTF-7
例えば、UTF-8 を UTF-7 に変換する場合には、まず UTF-32 にして、次に UTF-16 に変換してから、UTF-7 にする必要があります。
Q. UTF の種類についてもう少し詳しく教えて
次の通りです。
|
UTF | 表現可能最大値 | バイト長(*) | 別名 | コメント | 出典 |
|
UTF-1 | 0x7FFFFFFF | 1-5 | - | 破棄された | ISO 10646:1993 Annex G |
UTF-2 | 0x7FFFFFFF | 1-6 | UTF-8 | UTF-8 の昔の名前 | - |
|
UTF-5 | 0x7FFFFFFF | 1-8 | - | DNS への使用を想定 | draft-jseng-utf5-00.txt: UTF-5, a transformation format of Unicode and ISO 10646 (失効) |
UTF-6 | 0x0010FFFF | 1- | - | DNS への使用を想定。UTF-5 の改良版だから UTF-6。UTF の 6bit 版ではない。 | draft-ietf-idn-utf6-00.txt: UTF-6 - Yet Another ASCII-Compatible Encoding for IDN (失効) |
UTF-7 | 0x0010FFFF | 1-6 | - | Mail での使用を想定 | RFC2152: UTF-7 A Mail-Safe Transformation Format of Unicode |
UTF-8 | 0x7FFFFFFF | 1-6 | FSS-UTF | ファイルシステムでの使用を想定 | RFC2279: UTF-8, a transformation format of ISO 10646 |
UTF-9 | 0x7FFFFFFF | 1-4 | - | 36bitワード計算機用 | RFC4042: UTF-9 and UTF-18 Efficient Transformation Formats of Unicode (2005 Joke RFC) |
UTF-16 | 0x0010FFFF | 2,4 | - | - | RFC2781: UTF-16, an encoding of ISO 10646 |
UTF-17 | 0x0010FFFF | 8 | - | 64bit アーキテクチャ用。UTF-16 の次という意味から UTF-17 だと思われる。 UTF の 17bit 版ではない。 | draft-whistler-utf17-00.txt: Unicode Transformation Format Seventeen (失効) |
UTF-18 | 0x0002FFFF, 0x000E0000 - 0x000EFFFF | 2 | - | 36bitワード計算機用 | RFC4042: UTF-9 and UTF-18 Efficient Transformation Formats of Unicode (2005 Joke RFC) |
UTF-32 | 0x0010FFFF | 4 | - | - | Unicode TR19 |
|
(*)1バイト==8bit (octet)。UTF-9/18 の場合1バイト==9bit (nonet)。また識別用ヘッダを除いた長さ。 |
Q. UTF-1 はなぜ破棄されたの?
符号化すると2バイト以降に '/' が現れるのでファイルシステムといっしょに使えない、符号化文字列の途中から検索すると文字の開始位置がわからない、割り算が入るので重くなる、などの問題が UTF-1 を使うと発生しました。この結果が、UTF-2 (後に UTF-8 とされる) の開発につながっていきました。UTF-8 では符号化した2バイト以降には '/' が出現しません。その結果ファイルシステムと一緒に使用することができます。そのようなことから、FSS-UTF (File System Safe UTF) とも呼ばれるようになりました。
Q. UTF の具体的な変換方式を教えて
次の通りです。ただし U を UCS または Unicode のコードポイントを表すことにします。また B
xx という表現は左にある U の xx ビット目の値をあらわしています。数字の中の _ は見やすくするために便宜上挿入した記号です。
※参考:「漢字」の UTF16 による表現は 0x6F22 0x5B57 です。
UTF-1
変換方法
- 次の関数 T を定義します。
z | T(z) |
0x00-0x5D | z + 0x21 |
0x5E-0xBD | z + 0x42 |
0xBE-0xDE | z - 0xBE |
0xDF-0xFF | z - 0x60 |
- 次のように、U をバイナリ表現に変換します。
U の範囲 (16進) | 変換された表現(バイナリ) |
0000_0000-0000_009F | B07B06B05B04B03B02B01B00 |
0000_00A0-0000_00FF | 0xA0 B07B06B05B04B03B02B01B00 |
0000_0100-0000_4015 | 0xA1+y/0xBE T(y%0xBE) ただし y = U - 0000_0100 |
0000_4016-0003_8E2D | 0xF6+y/0xBE2 T(y/0xBE%0xBE) T(y%0xBE) ただし y = U - 0000_4016 |
0003_8E2E-7FFF_FFFF | 0xFC+y/0xBE4
T(y/0xBE3%0xBE)
T(y/0xBE2%0xBE)
T(y/0xBE%0xBE)
T(y%0xBE)
ただし y = U - 0003_8E2E |
変換例
-
漢字 (0x6F22 0x5B57) → 変換 →
y1=0x6F22-0x4016, y2=0x5B57-0x4016 →
y1=0x2F0C, y2=0x1B41 →
0xF6+0x2F0C/0xBE2 T(0x2F0C/0xBE%0xBE) T(0x2F0C%0xBE)
0xF6+0x1B41/0xBE2 T(0x1B41/0xBE%0xBE) T(0x1B41%0xBE) →
0xF6 T(0x3F) T(0x4A) 0xF6 T(0x24) T(0x89) →
0xF6 0x60 0x6B 0xF6 0x45 0xCB →
F6606BF645CB
UTF-5
変換方法
- 次の表を用いて、U をバイナリ表現に変換します。
U の範囲 (16進) | 変換された表現(バイナリ) |
0000_0000-0000_000F | 1 B03B02B01B00 |
0000_0010-0000_00FF | 1 B07B06B05B04 0 B03B02B01B00 |
0000_0100-0000_0FFF | 1 B11B10B09B08 0 B07B06B05B04 0 B03B02B01B00 |
... |
1000_0000-7FFF_FFFF | 1 B31B30B29B28 0 B27B26B25B24 ... 0 B03B02B01B00 |
- 次の表のように、バイナリの対応する文字を変換された値とします。C 言語で書くと、"0123456789ABCDEFGHIJKLMNOPQRSTUV"[バイナリ] の値となります。アルファベットは必ず大文字を使用します。
バイナリ | 0_0000 | 0_0001 | 0_0010 | 0_0011 | 0_0100 | 0_0101 | 0_0110 | 0_0111 |
変換値 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
バイナリ | 0_1000 | 0_1001 | 0_1010 | 0_1011 | 0_1100 | 0_1101 | 0_1110 | 0_1111 |
変換値 | 8 | 9 | A | B | C | D | E | F |
バイナリ | 1_0000 | 1_0001 | 1_0010 | 1_0011 | 1_0100 | 1_0101 | 1_0110 | 1_0111 |
変換値 | G | H | I | J | K | L | M | N |
バイナリ | 1_1000 | 1_1001 | 1_1010 | 1_1011 | 1_1100 | 1_1101 | 1_1110 | 1_1111 |
変換値 | O | P | Q | R | S | T | U | V |
変換例
-
漢字 (0x6F22 0x5B57) → "MF22LB57" (すなわちバイト列 4D4632324C423537)
UTF-6
この符号方式は、UTF-5 の次のバージョンと言う意味であって、UTF の 6bit 版ではない。IDN の符号化方式の一つのであるので、本来ここに記す必要はないが、参考までに記しておく。基本的な考えは、各文字の上位バイト、もしくは上位ニブルの一致した部分を省略し、変形UTF-5 変換を行う。変形UTF-5 は、各文字の上位にある不要な 0 を無視して変換するという方法である。基本的にドメインのホスト名のような短い文字列を対象としている。
変換方法
- 可変長16進符号化(UTF5VAR): 文字の上位から連続するニブル(4bit)の値 0 を除去した値に対し UTF-5 の変換を行う。
例
0x1234→0x1234→S234 |
0x0123→0x123→H23 |
0x0001→0x1→H |
0x0000->0x0→G |
- 次のように行う。
mask=0xffff
if(文字 '-' を除いた文字数が 1 つ){
//なにもしない
} else if(文字列中の'-'を除いた各文字の先頭バイトが V で一致している){
出力("Y"+UTF5VAR(V)) //識別符号 'Y' と共通バイトを出力
mask=0x00ff
} else if(文字列中の'-'を除いた各文字の先頭ニブルが V で一致している){
出力("Z"+UTF5VAR(V)) //識別符号 'Z' と共通ニブルを出力
mask=0x0fff
}
foreach(文字列のそれぞれの文字 C){
出力(UTF5VAR(C & mask))
}
変換例
-
ユニコード (0x30E6 0x30CB 0x30B3 0x30FC 0x30C9) →
mask=0xffff →
文字列中の先頭バイトが 0x30 で一致 →
V=0x30 → 出力("Y"+UTF5VAR(0x30)) mask=0x00ff →
出力(UTF5VAR(0x30E6 & 0x00ff))
出力(UTF5VAR(0x30CB & 0x00ff))
出力(UTF5VAR(0x30B3 & 0x00ff))
出力(UTF5VAR(0x30FC & 0x00ff))
出力(UTF5VAR(0x30C9 & 0x00ff)) → 結果 →
"Y"+UTF5VAR(0x30)+UTF5VAR(0xE6)+UTF5VAR(0xCB)+UTF5VAR(0xB3)+UTF5VAR(0xFC)+UTF5VAR(0xC9) →
Y J0 U6 SB R3 VC S9 →
YJ0U6SBR3VCS9
UTF-7
変換方法
- 次の文字集合を定義します。
名称 | 定義される文字群 |
set_D | ABC..XYZabc..xyz012..789'(),-./:? |
set_O | !"#$%&*;<=>@[]^_`{|} |
set_B | ABC..XYZabc..xyz012..789+/ |
- SPC (0x20), TAB (0x09), CR (0x0d), LF (0x0a) は、そのまま使用することが許されます。
- set_D に出現する文字はそのまま使用することが許されます。また同様に set_O の文字も許されますが、mail システムによっては支障が出ることがあります。
- それ以外の文字は、Unicode 文字列として set_B で示される文字を使って符号化します。符号化は UTF-16 で示される 16bit 単位の Unicode 文字列を基本として、base64 (RFC2045) エンコードをします。ただし、バイトが足りなくなったときに付加される '=' 記号は使用せずに、不足した場合は、ビット値 0を必要数補って符号化します。デコードされたときの最終文字が完全な Unicode をなしていないものは捨てます。もしそれが 0 でないなら、不正な形式ということになります。
符号化した場合、それを表すために先頭にシフト文字 '+' をつけます。この '+' の文字が (修正された) base64 エンコードの開始を示し、set_B 以外の文字 (例えば、CR LF などの制御文字も含む) が出現するまでが符号化されていることを示します。また明示的に終了を示すために、'-' 記号を使うことができます。この場合には '-' 記号は捨てられます。
特例として、'+-' は '+' 文字を表すことにします。'+' の後に、set_B または '-'以外の文字が続いた場合不正になります。
変換例
-
漢字 (0x6F22 0x5B57) →
01101111_00100010 01011011_01010111 →
set_D, set_O に当てはまらないので、base64 でエンコードする →
011011 110010 001001 011011 010101 11 →
011011 110010 001001 011011 010101 110000 →
27 50 9 27 21 48 →
b y J b V w →
"+byJbVw-" (すなわちバイト列 2B62794A6256772D)
UTF-8 / UTF-2
変換方法
- 次の表を用いて、U をバイナリ表現に変換します。
U の範囲 (16進) | バイト数 | 変換された表現(バイナリ) |
0000_0000-0000_007F | 1 |
0 B06B05B04B03B02B01B00 |
0000_0080-0000_07FF | 2 |
1 1 0 B10B09B08B07B06 1 0 B05B04B03B02B01B00 |
0000_0800-0000_FFFF | 3 |
1 1 1 0 B15B14B13B12
1 0 B11B10B09B08B07B06
1 0 B05B04B03B02B01B00 |
0001_0000-001F_FFFF | 4 |
1 1 1 1 0 B20B19B18
1 0 B17B16B15B14B13B12
1 0 B11B10B09B08B07B06
1 0 B05B04B03B02B01B00 |
0020_0000-03FF_FFFF | 5 |
1 1 1 1 1 0 B25B24
1 0 B23B22B21B20B19B18
1 0 B17B16B15B14B13B12
1 0 B11B10B09B08B07B06
1 0 B05B04B03B02B01B00 |
0400_0000-7FFF_FFFF | 6 |
1 1 1 1 1 1 0 B30
1 0 B29B28B27B26B25B24
1 0 B23B22B21B20B19B18
1 0 B17B16B15B14B13B12
1 0 B11B10B09B08B07B06
1 0 B05B04B03B02B01B00 |
- バイナリを、変換値とします。
変換例
-
漢字 (0x6F22 0x5B57) →
01101111_00100010 01011011_01010111 →
0110_111100_100010 0101_101101_010111 → 変換 →
1110_0110 10_111100 10_100010 1110_0101 10_101101 10_010111 →
1110_0110 1011_1100 1010_0010 1110_0101 1010_1101 1001_0111 →
E6BCA2E5AD97
UTF-9
PDP-10 などの計算機では、マシンワードが 36bit なので、主に 8bit が単位の既存の UTF-8、UTF-16、UTF-32 では、36 の約数ではないので、無駄なビットが出てしまい、効率がよくありません。そこで 36bit の約数である 9bit 単位の nonet ごとに処理するのが適切になります。それを口実に、2005 年のジョーク RFC (RFC4042) として提案されたのが UTF-9 です。
変換方法
- 次の表を用いて、U をバイナリ表現に変換します。
U の範囲 (16進) | 変換された表現(バイナリ) |
0000_0000-0000_00FF |
0 B07B06B05B04B03B02B01B00
|
0000_0100-0000_FFFF |
1 B15B14B13B12B11B10B09B08
0 B07B06B05B04B03B02B01B00
|
0001_0000-00FF_FFFF |
1 B23B22B21B20B19B18B17B16
1 B15B14B13B12B11B10B09B08
0 B07B06B05B04B03B02B01B00
|
0100_0000-7FFF_FFFF |
1 B31B30B29B28B27B26B25B24
1 B23B22B21B20B19B18B17B16
1 B15B14B13B12B11B10B09B08
0 B07B06B05B04B03B02B01B00
|
- バイナリを、変換値とします。
変換例
-
漢字 (0x226F 0x575B) →
00100010_01101111 01010111_01011011 → 変換 →
1_00100010_0_01101111 1_01010111_0_01011011 →
0122006F0157005B
UTF-16
変換方法
- 次の表を用いて、U をバイナリ表現に変換します。ただし、U が 0x1_0000 以上の値の場合には、U から 0x1_0000 を引いた値を用います。
U の範囲 (16進) | 変換された表現(バイナリ) |
0000_0000-0000_FFFF |
B15B14B13B12B11B10B09B08B07B06B05B04B03B02B01B00 |
0001_0000-0010_FFFF |
1 1 0 1 1 0 B19B18B17B16B15B14B13B12B11B10
1 1 0 1 1 1 B09B08B07B06B05B04B03B02B01B00 |
- バイナリを、変換値とします。16 bit 値をこのままの順に格納すると UTF-16BE となります。バイナリに示される 16bit 値のバイトをそれぞれ逆順にすると UTF-16LE となります。
変換例
-
漢字 (0x6F22 0x5B57) → 6F225B57
UTF-17
変換方法
- 次のように、U をバイナリ表現に変換します。
U の範囲 (16進) | 変換された表現(バイナリ) |
0000_0000 |
00111000
0 0 1 1 0 B20B19B18
0 0 1 1 0 B17B16B15
0 0 1 1 0 B14B13B12
0 0 1 1 0 B11B10B09
0 0 1 1 0 B08B07B06
0 0 1 1 0 B05B04B03
0 0 0 0 0 B02B01B00 |
0000_0001-0010_FFFF |
00111000
0 0 1 1 0 B20B19B18
0 0 1 1 0 B17B16B15
0 0 1 1 0 B14B13B12
0 0 1 1 0 B11B10B09
0 0 1 1 0 B08B07B06
0 0 1 1 0 B05B04B03
0 0 1 1 0 B02B01B00 |
- バイナリを、変換値とします。
変換例
-
漢字 (0x226F 0x575B) →
00100010_01101111 01010111_01011011 → 変換 →
0_010_001_001_101_111 0_101_011_101_011_011 →
00111000 00110000 00110000 00110010 00110001 00110001 00110101 00110111
00111000 00110000 00110000 00110101 00110011 00110101 00110011 00110011 →
38 30 30 32 31 31 35 37
38 30 30 35 33 35 33 33 →
38303032313135373830303533353333 (このバイナリをダンプすると "8002115780053533" という ASCII 文字列に見える)
UTF-18
PDP-10 などの計算機では、マシンワードが 36bit なので、主に 8bit が単位の既存の UTF-8、UTF-16、UTF-32 では、36 の約数ではないので、無駄なビットが出てしまい、効率がよくありません。そこで 36bit の約数である 9bit 単位の nonet ごとに処理するのが適切になります。それを口実に、2005 年のジョーク RFC (RFC4042) として提案されたのが UTF-18 です。なお、UTF-18 は UTF-9 とは違い、0x2FFFF までのコードポイント、及び 0xE0000-0xEFFFF までのコードポイントしか扱うことができません。
変換方法
- 次の表を用いて、U をバイナリ表現に変換します。
U の範囲 (16進) | 変換された表現(バイナリ) |
0000_0000-0002_FFFF |
B17B16B15B14B13B12B11B10B09
B08B07B06B05B04B03B02B01B00
|
000E_0000-000E_FFFF |
1 1 B15B14B13B12B11B10B09
B08B07B06B05B04B03B02B01B00
|
- バイナリを、変換値とします。
変換例
-
漢字 (0x226F 0x575B) →
00100010_01101111 01010111_01011011 → 変換 →
00_00100010_01101111 00_01010111_01011011 →
00226F00575B
UTF-32
変換方法
- 次のように、U をバイナリ表現に変換します。
U の範囲 (16進) | 変換された表現(バイナリ) |
0000_0000-0010_FFFF |
00000000
0 0 0 B20B19B18B17B16
B15B14B13B12B11B10B09B08
B07B06B05B04B03B02B01B00 |
- バイナリを、変換値とします。このままの順だと UTF-32BE となります。バイナリに示されるバイトの順を逆にすると UTF-32LE となります。
変換例
-
漢字 (0x6F22 0x5B57) → 00006F2200005B57