Unicode 和 UTF-8 、UTF-16是什么關系呢?這是我最近好奇的一個問題。
Unicode
Unicode 是計算機科學領域里的一項業界標準,包括字符集(包含來自世界各國各地的文字字符)和編碼方案(將每個字符唯一映射到一個二進制編碼)。
Unicode 編碼方式
Unicode 的基本元素是碼位(code point),共包含1114112個碼位。碼位通過使用數值表示,數值格式為:U+hhhh,其中每個h代表一個十六進制數字。
Unicode 的所有碼位組成了一個編碼空間。在Unicode編碼空間,Unicode碼位分為17組編排,每組稱為平面(Plane),而每平面擁有65536個碼位。如下圖所示:
Unicode 遵守既定規則把世界上的字符一一映射到碼位中。被使用的碼位,其碼點值就是對應字符的Unicode編碼。如, U+0041 表示拉丁字母 “A”;U+40000 由于沒使用,不表示任何字符。
截止目前,才128237 個碼位被使用——編碼空間的 12% 被賦值,在后面還有很多空間可應對新出現的字符。
Unicode 實現方式
Unicode 規定了字符的編碼,但是沒有規定如何實現字符的編碼。實現編碼的方式稱為Unicode 轉換格式(Unicode Transformation Format,簡稱為UTF)。
Unicode 編碼范圍從 U+0000 到 U+10FFFF。對于計算機而言,實現Unicode 編碼的最簡單方式是使用32-bit表示。但是每個字符使用4個字節表示會造成浪費。當你處理大量文本的時候,使用 32-bit 存儲 Unicode 字符會占用大量額外存儲、內存、帶寬等,因為大多字符的第1和第2個字節都是0。
在日常,Unicode編碼的實現方式主要有:UTF-8、UTF-16。
UTF-8
UTF-8是一種可變長度字符編碼方式,以8-bit 為單元,使用1至4個字節為每個字符編碼。
UTF-8編碼規則如下:
Unicode編碼范圍 (十六進制) | UTF-8編碼占用字節 | UTF-8 編碼(二進制) |
---|---|---|
U+0000 - U+007F | 1 | 0xxxxxxx |
U+0080 - U+07FF | 2 | 110xxxxx 10xxxxxx |
U+0800 - U+FFFF | 3 | 1110xxxx 10xxxxxx 10xxxxxx |
U+10000 - U+10FFFF | 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
根據字符所在編碼范圍,確定其所占字節數
單字節的字符,字節的第一位設為0
n個字節的字符(n>1),第一個字節的前n位設為1,第n+1位設為0,后面字節的前兩位都設為10,這n個字節的其余空位填充該字符unicode碼,高位用0補足
下面,以漢字“魚”為例,演示如何實現UTF-8編碼。
“魚”的Unicode編碼是9C7C
(1001 1100 0111 1100
),根據上表,9C7C
的UTF-8編碼需要占用3個字節,其格式為1110xxxx 10xxxxxx 10xxxxxx
。然后,從“魚”的最后一個二進制位開始,依次從后向前填入格式中的x
,多出的位補0
。這樣,就得到“魚”的UTF-8編碼:11101001 10110001 10111100
,轉換為十六進制就是:E9B1BC
UTF-16
UTF-16是一種可變長度字符編碼方式,以16-bit 為單元,使用2個或4個字節為每個字符編碼。
UTF-16的編碼規則如下:
Unicode編碼范圍 (十六進制) | UTF-16編碼占用字節 | UTF-16 編碼(二進制) |
---|---|---|
U+0000 - U+FFFF | 2 | xxxxxxxx xxxxxxxx |
U+10000 - U+10FFFF | 4 | 110110yyyyyyyyyy 110111xxxxxxxxxx |
把字符的Unicode編碼記作 U
若U < 0x10000,字符的UTF-16編碼就是U對應的16位二進制:xxxxxxxx xxxxxxxx
若U ≥ 0x10000,則把字符拆分為2部分(U+10000 ~ U+10FFFF的空間大小是2^20),前十位映射到U+D800U+DBFF,后十位映射到U+DC00U+DFFF。具體拆分計算如下:
計算U'=U-0x10000,
將U'寫成二進制形式:yyyy yyyy yyxx xxxx xxxx,
按照
110110yyyyyyyyyy 110111xxxxxxxxxx
格式填入,即得到U的UTF-16編碼(二進制):110110yyyyyyyyyy 110111xxxxxxxxxx
- 在基本平面(0x00000xFFFF)內,U+D800U+DFFF是一個空段,即這些碼位不對應任何字符
- 輔助平面(0x10000~0x10FFFF)的字符位共有2^20個
- U+D800U+DBFF,空間大小是2^10;U+DC00U+DFFF,空間大小是2^10。二者組合起來,正好可以表示輔助平面的字符。即:輔助平面的字符可以拆分為兩個基本平面的字符表示——字符前10位映射在U+D800到U+DBFF,后10位映射在U+DC00到U+DFFF
下面,以漢字“魚”和字符“??”為例,演示如何實現UTF-16編碼。
“魚”的Unicode編碼是9C7C
,根據上表,9C7C < 0x10000
,其UTF-16編碼就是其Unicode編碼:1001 1100 0111 1100
,即0x9C7C。
“??”的Unicode編碼是1D300
,根據上表,1D300 > 0x10000
,然后減去0x10000,得到0xD300,按yyyy yyyy yyxx xxxx xxxx
格式寫成二進制是:0000 1101 0011 0000 0000
,然后按照110110yyyyyyyyyy 110111xxxxxxxxxx
格式填入,得到“??”的UTF-16編碼(二進制):1101100000110100 1101111100000000
,即0xD834 0xDF00
。
結論
回到先前的疑問:Unicode 和 UTF-8 、UTF-16是什么關系呢?
打個比喻,它們理論和實現的關系:Unicode制定了的理論,UTF-8和UTF-16是具體的實現方案。
而更形象的比喻則是:Unicode相當于中文, UTF-8、 UTF-16等相當于 行書、 楷書、草書等各種書寫方式。
參考資料:
https://zh.wikipedia.org/wiki/Unicode
https://zh.wikipedia.org/wiki/UTF-8
https://zh.wikipedia.org/wiki/UTF-16
http://www.ruanyifeng.com/blog/2014/12/unicode.html
http://blog.jobbole.com/111261/
http://blog.csdn.net/softman11/article/details/6124345
https://www.zhihu.com/question/23374078