ASCII碼
使用一個位元組(8位),對128個字元進行編碼;
最高位始終為0;
碼數範圍為0000_0000(0x00)
到0111_1111(0x7F)
;
Unicode
開始的編碼設計
使用兩個位元組(16位),對65536個字元進行編碼;
範圍為0000_0000_0000_0000(0x0000)
到1111_1111_1111_1111(0xFFFF)
;
而0x0000 - 0x007F
對應的字元,與ASCII碼保持一致;
最終的編碼設計
由於世界上的字元,超過了65536個,所以開始只用兩個位元組的設計已經不足夠了,需要擴充套件;
最終擴充套件如下:
-
基本多語言平面(BMP, Basic Multilingual Plane)
和開始的設計一致,用兩個位元組來編碼,碼數範圍
0x0000 - 0xFFFF
;但是,在這個範圍裡,有預留
0xD800 - 0xDFFF
的碼數,他們不代表任何字元,僅用於作為增補平面的代理對而存在; -
增補平面(SP, Supplementary Plane)
超出
BMP
所能表示的字元,改用如下範圍:0x10000 - 0x10FFFF
來編碼;Unicode編者認為這個範圍已經足夠全世界的字元編碼了,因為這足夠表示一百萬多個字元了;
代理對(surrogate pair)
預留的0xD800 - 0xDFFF
,分為兩部分:
- 高位
0xD800 - 0xDBFF
- 低位
0xDC00 - 0xDFFF
這樣做的目的,是為了UTF-16編碼方式;
一個高位加一個低位,共四個位元組,定義了SP中的字元的UTF-16編碼;
碼點(code point)
Unicode編碼中,一個字元所對應的碼數,稱為該字元的碼點;
通常在計算機的字元和字串中,使用\u碼點
的形式來轉義碼點,來表示一個Unicode編碼的碼點所對應的字元;
UTF-16
請注意,Unicode編碼的碼點,是人為約定的對字元的編碼方式;
但是計算機只認二進位制,所以如何將Unicode定義的字元的碼點,編碼為計算機實際儲存的二進位制串,以及如何從一串二進位制串,解碼成Unicode定義的字元的碼點,就是UTF-16
要做的事情;
UTF-16的16代表最小的編碼單位是16位二進位制串;
編碼
分為兩種情況:
-
BMP中的字元
直接用Unicode定義的碼點作為UTF-16編碼即可;
-
SP中的字元
使用兩個16位二進位制串進行編碼,即採用四個位元組來編碼;
現在假設有一個字元,其Unicode定義的碼點為
0xAAAAA
,對其進行如下操作:- u = 0xAAAAA - 0x10000;
- 將u寫成二進位制串:
yyyy_yyyy_yyxx_xxxx_xxxx
; - 則該字元的UTF-16編碼為:
1101_10yy_yyyy_yyyy 1101_11xx_xxxx_xxxx
;
SP
的UTF-16編碼的兩個16位二進位制串:第一個16位串的前六位固定是
1101_10
,結合yy
的範圍(00 - 11
),即1101_1000 - 1101_1011
,此範圍即是代理對的高位的前兩位0xD8 - 0xDB
;第二個16位串的前六位固定是
1101_11
,結合xx
的範圍00 - 11
,即1101_1100 - 1101_1111
,此範圍即是代理對的低位的前兩位的範圍0xDC - 0xDF
;再結合各自後面八位二進位制串的範圍
0000_0000 - 1111_1111
,就可以得到各自完整的代理對;也就是說,
SP
的UTF-16的編碼結果,即為高位+低位的四個位元組的代理對;
解碼
只要看一個16位二進位制串的頭八位,是否在代理對的範圍即可;
-
不在代理對的範圍
說明是
BMP
中的字元,直接對應Unicode碼點找到對應的字元即可; -
在代理對的範圍
說明是
SP
中的字元,再根據頭六位確定好代理對的高低位,去除各自的前六位,組成20位二進位制串,再加上
0x10000
即為Unicode定義的碼點,即可找到對應的字元;
UTF-8
UTF-8是不同於UTF-16的另一種對Unicode的編解碼方式;
不同之處就在於,UTF-8的8代表最小的編碼單位是8位二進位制串;
編碼
UTF-8對碼點的編碼方式如下:
-
碼點範圍
0x0000 - 0x007F
UTF-8編碼為二進位制串
0xxx_xxxx
,與ASCII碼保持一致,長度為1個位元組; -
碼點範圍
0x0080 - 0x07FF
UTF-8編碼為二進位制串
110x_xxxx 10xx_xxxx
,長度為2個位元組; -
碼點範圍
0x0800 - 0xFFFF
UTF-8編碼為二進位制串
1110_xxxx 10xx_xxxx 10xx_xxxx
,長度為3個位元組; -
碼點範圍
0x10000 - 0x10FFFF
UTF-8編碼為二進位制串
1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx
,長度為4個位元組;
假設現在有一個字元,碼點在範圍0x0800 - 0xFFFF
中:
- 將其碼點寫成二進位制串:
xxxx_yyyy yyzz_zzzz
; - 則UTF-8編碼的第一個位元組為
1110_xxxx
; - 第二個位元組為
10yy_yyyy
; - 第三個位元組為
10zz_zzzz
;
解碼
只要看第一個位元組的首位即可:
-
首位為0
說明在碼點範圍
0x0000 - 0x007F
,直接對應Unicode碼點找到對應的字元即可; -
首位為1,再看從首位開始,遇到第一個0結束,一共有幾個1
- 兩個1,說明UTF-8編碼長度為2個位元組
- 三個1,說明UTF-8編碼長度為3個位元組
- 四個1,說明UTF-8編碼長度為4個位元組
- 去除對應位元組的固定位,組合為一個二進位制串,找到對應Unicode碼點的字元即可;
程式碼單元(code unit)
不同的UTF編碼,所對應的編碼單位的長度不同;
UTF-16的編碼單位的長度為16位二進位制;
UTF-8的編碼單位的長度為8位二進位制;
這個編碼單位稱為程式碼單元;
比如對於UTF-16的編碼:
在BMP
中,一個字元所對應的UTF-16的16位二進位制串,稱為該字元的程式碼單元;
而在SP
中,一個字元所對應的UTF-16的兩個16位二進位制串,稱為該字元的一對程式碼單元;
而對於UTF-8的編碼:
在碼點範圍0x0000 - 0x007F
中,一個字元所對應的UTF-8的4個位元組,稱為該字元的4個程式碼單元;
在碼點範圍0x0080 - 0x07FF
中,一個字元所對應的UTF-8的4個位元組,稱為該字元的4個程式碼單元;
在碼點範圍0x0800 - 0xFFFF
中,一個字元所對應的UTF-8的4個位元組,稱為該字元的4個程式碼單元;
在碼點範圍0x10000 - 0x10FFFF
中,一個字元所對應的UTF-8的4個位元組,稱為該字元的4個程式碼單元;
也就是說,隨著UTF編碼形式的不同,同一個字元的碼點,會有不同個數的程式碼單元;