上個月,我做了一次分享,詳細介紹了Unicode字符集,以及JavaScript語言對它的支援。下面就是這次分享的講稿。
![](/blogimg/asset/2014/bg2014121102.jpg)
## 一、Unicode是什麼?
Unicode源於一個很簡單的想法:將全世界所有的字元包含在一個集合裡,計算機只要支援這一個字符集,就能顯示所有的字元,再也不會有亂碼了。
![](/blogimg/asset/2014/bg2014121103.jpg)
**它從0開始,為每個符號指定一個編號,這叫做"碼點"(code point)。**比如,碼點0的符號就是null(表示所有二進位制位都是0)。
U+0000 = null
上式中,U+表示緊跟在後面的十六進位制數是Unicode的碼點。
![](/blogimg/asset/2014/bg2014121115.png)
目前,Unicode的最新版本是7.0版,一共收入了109449個符號,其中的中日韓文字為74500個。可以近似認為,全世界現有的符號當中,三分之二以上來自東亞文字。比如,中文"好"的碼點是十六進位制的597D。
U+597D = 好
這麼多符號,Unicode不是一次性定義的,而是分割槽定義。每個區可以存放65536個(216)字元,稱為一個平面(plane)。目前,一共有17個(25)平面,也就是說,整個Unicode字符集的大小現在是221。
最前面的65536個字元位,稱為基本平面(縮寫BMP),它的碼點範圍是從0一直到216-1,寫成16進位制就是從U+0000到U+FFFF。所有最常見的字元都放在這個平面,這是Unicode最先定義和公佈的一個平面。
剩下的字元都放在輔助平面(縮寫SMP),碼點範圍從U+010000一直到U+10FFFF。
![](/blogimg/asset/2014/bg2014121104.png)
## 二、UTF-32與UTF-8
Unicode只規定了每個字元的碼點,到底用什麼樣的位元組序表示這個碼點,就涉及到編碼方法。
**最直觀的編碼方法是,每個碼點使用四個位元組表示,位元組內容一一對應碼點。這種編碼方法就叫做UTF-32。**比如,碼點0就用四個位元組的0表示,碼點597D就在前面加三個位元組的0。
U+0000 = 0x0000 0000 0000 0000
U+597D = 0x0000 0000 0000 597D
![](/blogimg/asset/2014/bg2014121116.png)
UTF-32的優點在於,轉換規則簡單直觀,查詢效率高。缺點在於浪費空間,同樣內容的英語文字,它會比ASCII編碼大四倍。這個缺點很致命,導致實際上沒有人使用這種編碼方法,HTML 5標準就明文規定,網頁不得編碼成UTF-32。
![](/blogimg/asset/2014/bg2014121105.png)
人們真正需要的是一種節省空間的編碼方法,這導致了UTF-8的誕生。**UTF-8是一種變長的編碼方法,字元長度從1個位元組到4個位元組不等。**越是常用的字元,位元組越短,最前面的128個字元,只使用1個位元組表示,與ASCII碼完全相同。
編號範圍 | 位元組 |
0x0000 - 0x007F | 1 |
0x0080 - 0x07FF | 2 |
0x0800 - 0xFFFF | 3 |
0x010000 - 0x10FFFF | 4 |
由於UTF-8這種節省空間的特性,導致它成為網際網路上最常見的網頁編碼。不過,它跟今天的主題關係不大,我就不深入了,具體的轉碼方法,可以參考我多年前寫的[《字元編碼筆記》](http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html)。
## 三、UTF-16簡介
UTF-16編碼介於UTF-32與UTF-8之間,同時結合了定長和變長兩種編碼方法的特點。
它的編碼規則很簡單:基本平面的字元佔用2個位元組,輔助平面的字元佔用4個位元組。**也就是說,UTF-16的編碼長度要麼是2個位元組(U+0000到U+FFFF),要麼是4個位元組(U+010000到U+10FFFF)。**
![](/blogimg/asset/2014/bg2014121106.png)
於是就有一個問題,當我們遇到兩個位元組,怎麼看出它本身是一個字元,還是需要跟其他兩個位元組放在一起解讀?
說來很巧妙,我也不知道是不是故意的設計,在基本平面內,從U+D800到U+DFFF是一個空段,即這些碼點不對應任何字元。因此,這個空段可以用來對映輔助平面的字元。
具體來說,輔助平面的字元位共有220個,也就是說,對應這些字元至少需要20個二進位制位。UTF-16將這20位拆成兩半,前10位對映在U+D800到U+DBFF(空間大小210),稱為高位(H),後10位對映在U+DC00到U+DFFF(空間大小210),稱為低位(L)。這意味著,一個輔助平面的字元,被拆成兩個基本平面的字元表示。
![](/blogimg/asset/2014/bg2014121117.png)
**所以,當我們遇到兩個位元組,發現它的碼點在U+D800到U+DBFF之間,就可以斷定,緊跟在後面的兩個位元組的碼點,應該在U+DC00到U+DFFF之間,這四個位元組必須放在一起解讀。**
## 四、UTF-16的轉碼公式
Unicode碼點轉成UTF-16的時候,首先區分這是基本平面字元,還是輔助平面字元。如果是前者,直接將碼點轉為對應的十六進位制形式,長度為兩位元組。
U+597D = 0x597D
如果是輔助平面字元,Unicode 3.0版給出了轉碼公式。
H = Math.floor((c-0x10000) / 0x400)+0xD800
L = (c - 0x10000) % 0x400 + 0xDC00
![](/blogimg/asset/2014/bg2014121107.png)
以字元