Unicode

はじめは16ビット→いろいろあって21ビット

16ビット(2バイト)あれば 216=65536 文字を登録できます。漢字を知っている人ならすぐに足りないと感じますが、最初はこれで全世界の文字を登録できると思っていた様です。

当然不足して、32ビット(4バイト)にする案もあったのですが、いろいろあって結局 21ビット(3バイトより少ない)に落ち着きました。 0~10FFFF です。

16進の1桁は4ビットですから、5桁で20ビット、それに1ビットを足して21ビットです。21ビットで表せる文字数は、221=2097152 ですが、それは 1FFFFF まで使う場合です。実際には 10FFFF までですので 1114112 文字になります。

第0面から第16面までの17面が使えて、1面は16ビットですから、216×17=1114112 です。

Unicodeスカラ値21ビットの使用状況 (1面で 65536 文字)
Unicodeスカラ値の範囲文字説明
0000~ FFFF第0面基本多言語面(BMP)
10000~ 1FFFF第1面追加多言語面(SMP)
20000~ 2FFFF第2面追加漢字面(SIP)
30000~ 3FFFF第3面第3漢字面(未使用)(TIP)
40000~ 4FFFF第4面未使用・用途未定
50000~ 5FFFF第5面未使用・用途未定
60000~ 6FFFF第6面未使用・用途未定
70000~ 7FFFF第7面未使用・用途未定
80000~ 8FFFF第8面未使用・用途未定
90000~ 9FFFF第9面未使用・用途未定
A0000~ AFFFF第10面未使用・用途未定
B0000~ BFFFF第11面未使用・用途未定
C0000~ CFFFF第12面未使用・用途未定
D0000~ DFFFF第13面未使用・用途未定
E0000~ EFFFF第14面追加特殊用途面(SSP)
F0000~ FFFFF第15面私用面
100000~10FFFF第16面私用面

Unicodeスカラ値

文字セットは16進数にU+をつけて U+0000~U+10FFFF で表します。これをUnicodeスカラ値といいます。

第0面は16進数で4桁で U+0000~U+FFFF と表します。この16ビット(2バイト)で表現できる部分は65536文字で、ここには基本的な文字を登録してあり、基本多言語面(BMP)と呼ばれます。

BMPの他に16ビット65536文字の面が16あります。0000~FFFF の前に 1,2,3,...D,E,F,10 と 16進数で 1から16 の番号がついていると考えれば理解できると思います。

UTF-16 エンコード

JISで面区点で示された文字を実際に使うときに、ISO-2022-JP,Shift_JIS,EUC-JPなどさまざまなエンコーディングを使用したのと同様に、Unicodeにもたくさんの方式があります。主に使われているのは UTF-8 と UTF-16 の2つです。

UTF-16 具体例

Unicodeスカラ値の第0面(基本多言語面)は、ほとんどそのままUTF-16の文字コード(2バイト=16ビット)になります。

€・じょう)など第0面にない漢字は、サロゲートペアで表します。予約してある2048個のコードを2つ組み合わせて4バイト=32ビットにして表示する仕組みです。

Unicodeスカラ値 と UTF-16
Unicodeスカラ値 文字 UTF-16 説明
U+0041 A 0041 スカラ値がそのまま文字コードになる文字
U+0061 a 0061
U+00E8 è 00E8
U+042F Я 042F
U+2162 2162
U+3042 3042
U+4E9C 4E9C
U+D558 D558
U+2000B 𠀋(じょう) D840 DC0B サロゲートペアを使う文字
U+20BB7 𠮷(よし) D842 DFB7
U+29E3D 𩸽(ほっけ) D867 DE3D

UTF-16 文字の割り当てとサロゲートペアの位置

Unicodeスカラ値の第0面が、ほとんどそのままUTF-16のコードになります。下の表の文字の配置表はそのままUnicodeスカラ値の第0面の配置表でもあります。

上位代用下位代用の部分は文字が直接割り当てられていません。上位代用の2バイトと下位代用の2バイトを組み合わせて第1面から第16面までの文字を表します。これをサロゲートペア(代用対)といいます。

UTF-16はBMPの一部を拡張している。代用対の位置を確認
UTF-16の範囲x000~x3FFx400~x7FFx800~xBFFxC00~xFFF
0000~0FFFASCII,ラテン拡張AB,ギリシア,キリル,アラビア,タイ
1000~1FFFミャンマー,ハングル字母,クメール,モンゴル
2000~2FFF一般句読点,通貨記号,矢印,数学記号,技術用記号,ラテン拡張C
3000~3FFFCJKの記号,平仮名,片仮名,CJK統合漢字拡張A
4000~4FFFCJK統合漢字拡張A, CJK統合漢字
5000~5FFFCJK統合漢字
6000~6FFFCJK統合漢字
7000~7FFFCJK統合漢字
8000~8FFFCJK統合漢字
9000~9FFFCJK統合漢字
A000~AFFFキリル拡張,ラテン拡張D,パスパ,ジャワ,ハングル
B000~BFFFハングル
C000~CFFFハングル
D000~DFFFハングル上位代用下位代用
E000~EFFF私用領域 (外字領域)
F000~FFFF私用領域 (外字領域),CJK互換漢字,特殊用途文字

サロゲートペアの上位は U+D800~U+DBFF, 下位は U+DC00~U+DFFF で、それぞれ1024字分あります。組み合わせると 1024×1024=1048576文字になります。これで第1面から第16面までの文字 65536×16=1048576文字を表すことができます。

UTF-16 サロゲートペアの計算法

第1面から第16面のユニコードスカラ値を$unicodeとするとサロゲートペアの上位と下位はそれぞれ次のように計算します。

ただし0xは16進数であることを表し、/は切り捨てをする割り算、%は割り算のあまりを求める計算です。

上位 = ($unicode - 0x10000) / 0x400 + 0xD800;
下位 = ($unicode - 0x10000) % 0x400 + 0xDC00;

元に戻すには

$unicode = 0x10000 + (上位 - 0xD800) * 0x400 + (下位 - 0xDC00);

UTF-16 はもともと全部の文字を2バイトで表現できると思っていたときの方式です。Unicodeスカラ値の16進数4桁(2バイト)をそのまま使っていました。

しかし、これでは不足だと分かってから、まだ文字を登録していなかった 0xD800~0xDFFF の2048文字分の領域に直接文字を割り付けずに、前半の0xD800~0xDBFFから1文字分、後半0xDC00~0xDFFFから1文字分のコードを組み合わせて2文字分つまり4バイトで1文字ということにしました。

これにより2048文字分だった範囲を使って 1024×1024=1048576 文字分を生み出したことになります。

UTF-16で表せる文字数の合計を計算する
16ビットで表せる文字数 21665536
サロゲートペアの部分には直接文字を割りつけない1024+1024-2048
サロゲートペアで登録できる文字数1024×10241048576
UTF-16で表せる文字数の合計1112064

UTF-8 エンコード

UTF-8は1~4バイト(初期の定義では6バイトまであった)の可変長コードです。

Unicodeスカラ値とUTF-8
Unicodeスカラ値 文字 UTF-8 説明
U+0041 A 41 1バイト
U+0061 a 61
U+00E8 è C3 A8 2バイト
U+042F Я D0 AF
U+2162 E2 85 A2 3バイト
U+3042 E3 81 82
U+4E9C E4 BA 9C
U+D558 ED 95 98
U+2000B 𠀋(じょう) F0 A0 80 8B 4バイト
U+20BB7 𠮷(よし) F0 A0 AE B7
U+29E3D 𩸽(ほっけ) F0 A9 B8 BD

UTF-8 文字の区切りがわかる仕組み

それぞれの1バイトの値で、それが文字の最初のバイトであるか、2バイト目以降のバイトであるかがわかるようになっています。

コード カテゴリ 備考
00-7x 1バイト文字 US-ASCIIにおなじ
8x,9x,Ax,Bx 多バイト文字の2バイト目以降
Cx,Dx 2バイト文字の開始バイト
Ex 3バイト文字の開始バイト 漢字はおおむねこれで開始
Fx 4バイト以上の文字の開始バイト F0-F7は4バイト、(F8-FBは5バイト、FC-FDは6バイト)

UTF-8 への換算

U+XXXX と表されるUnicodeスカラ値からUTF-8への換算方法です。もちろん逆も可能。

ビットの状態にしてから切り貼りをしています。

Unicodeスカラ値 二進表現 UTF-8 の二進表現
(1) U+ 00 7Fまで 0000 0000 0ppp pppp 0ppp pppp
(2) U+ 07 FFまで 0000 0sss pppp pppp 110s sspp 10pp pppp
(3) U+ FF FFまで ssss ssss pppp pppp 1110 ssss 10ss sspp 10pp pppp
(4) U+ F FF FFまで tttt ssss ssss pppp pppp 1111 00tt 10tt ssss 10ss sspp 10pp pppp
(4') U+ 10 FF FFまで 1 0000 ssss ssss pppp pppp 1111 0100 1000 ssss 10ss sspp 10pp pppp

UTF-8 への換算の具体例

(3)の領域から2つ、(4)から1つ例をあげます。

文字      : 聖
スカラー値: U+8056
          : 8    0    5    6
二進にして: 1000 0000 0101 0110
並べかえて: 1110 1000 1000 0001 1001 0110
16進にして: E    8    8    1    9    6
UTF-8     : E8 81 96
文字      : 愛
スカラー値: U+611B
          : 6    1    1    B
二進にして: 0110 0001 0001 1011
並べかえて: 1110 0110 1000 0100 1001 1011
16進にして: E    6    8    4    9    B
UTF-8     : E6 84 9B
文字      : 或
スカラー値: U+2123D
          : 2    1    2    3    D
二進にして: 0010 0001 0010 0011 1101
並べかえて: 1111 0000 1010 0001 1000 1000 1011 1101
16進にして: F    0    A    1    8    8    B    D
UTF-8     : F0 A1 88 BD

ちなみに、UTF-8に上記の方法で換算するときは、必ずUnicodeスカラ値から換算します。サロケートペアから換算してはいけません。

UTF-8 と UTF-16 の比較

UTF-8 と UTF-16
Unicodeスカラ値 文字 説明 UTF-8 UTF-16
U+0041 A ラテン文字 41 0041
U+0061 a ラテン文字 61 0061
U+00E8 è ラテン文字 C3 A8 00E8
U+042F Я キリル文字(ロシア) D0 AF 042F
U+05D0 א ヘブライ文字 D7 90 05D0
U+0905 デーヴァナーガリ文字 E0 A4 85 0905
U+0E04 タイ文字 E0 B8 84 0E04
U+2162 ローマ数字 E2 85 A2 2162
U+3042 ひらがな E3 81 82 3042
U+4E9C 漢字(あ) E4 BA 9C 4E9C
U+D558 ハングル ED 95 98 D558
U+103A0 𐎠 楔形文字 F0 90 8E A0 D800 DFA0
U+2000B 𠀋 漢字(じょう) F0 A0 80 8B D840 DC0B
U+20BB7 𠮷 漢字(よし) F0 A0 AE B7 D842 DFB7
U+29E3D 𩸽 漢字(ほっけ) F0 A9 B8 BD D867 DE3D