Unicode是一個字元庫,裡面收錄了全世界絕大多數的字元,並用0,1,2,,,n對每一個字元進行唯一的編號,這些編號我們稱之為碼點。
當前Unicode字元被分為17組編排,每組稱為一個平面,每個平面擁有65536(即2^16)個字元。
包含最前面的65536個字元,即碼點範圍從0 ~ 2^16 - 1,稱為基本多文種平面(Basic Multilingual Plane, BMP)。所有最常見的字元都在這個平面裡面,這是Unicode最先定義和公佈的一個平面。剩下的16組平面稱為輔助平面。
17組平面可以包含2^(5+16)個字元,即需要21位的編碼空間,比3位元組略少。
Unicode只是一個符號集,它只規定了符號的二進位制程式碼,卻沒有規定這個二進位制程式碼應該如何儲存。考慮一些極端的例子: 1.用二進位制程式碼表示Unicode中碼點為1的字元,使用1個位元組就是00000001;使用2個位元組就是00000000 00000001;使用3個位元組就是00000000 00000000 00000001;使用4個位元組就是00000000 00000000 00000000 00000001。很顯然如果使用四個位元組儲存該字元,將造成極大的空間浪費。 2.用二進位制程式碼表示Unicode中碼點為100000的字元,至少需要3個位元組,即 00000001 10000110 10100000,當然也可以用4個位元組00000000 00000001 10000110 10100000來表示。
考慮到系統平臺的差異以及出於節省儲存空間的因素,Unicode並沒有對使用多少個位元組來儲存一個字元對應的二進位制程式碼做出強制的規定,因此Unicode的實現方式有多種,而Unicode的實現方式稱為Unicode轉換格式(Unicode Transformation Format,簡稱為UTF)
我們平時常見的編碼方案,比如ASCII、UTF-8、UTF-16、UTF-32都是對Unicode的不同實現。 ASCII編碼:使用1個位元組來表示一個字元,該編碼方案僅適用於對Unicode中的前128位字元,適用於西文文件。 UTF-32編碼:使用4個位元組表示一個字元,理論上4個位元組可表示2^32 = 4,294,967,296,遠超當今的Unicode所接納的字元,因此可以完全對應Unicode編碼。該編碼的優點是轉換規則簡單直觀,查詢效率高,但是也有致命的缺點,浪費空間,同樣內容的英語文字,它會比ASCII編碼大四倍,目前基本上沒有什麼場景去使用這種編碼方式。 UTF-8編碼:在衡量一個程式設計好壞時,時間和空間這兩個維度的複雜度是永遠繞不開的。如果你記憶體足夠大,你完全可以用空間換時間,但是從編碼設計角度來說,人們真正需要的還是一種節省空間的編碼方案,這直接導致了目前使用最多的utf-8編碼方案的誕生。UTF-8是一種變長的編碼方法,字元長度從1個位元組到4個位元組不等。越是常用的字元,位元組越短,最前面的128個字元,只使用1個位元組表示,與ASCII碼完全相同(為了方便書寫,後面涉及到的二級制程式碼統一用16進製表示)。
UTF-16編碼:介於UTF-32與UTF-8之間,同時結合了定長和變長兩種編碼方法的特點。它的編碼規則很簡單,基本平面的字元佔用2個位元組,輔助平面的字元佔用4個位元組。也就是說,UTF-16的編碼長度要麼是2個位元組(U+0000到U+FFFF),要麼是4個位元組(U+010000到U+10FFFF)。前面介紹過了基本平面包含了 65536 = 2^16 個字元,因此用兩個位元組去表示非常合適。
那麼緊接著問題就來了,計算機怎麼知道當前的這個字元,它應該讀取兩個位元組還是四個位元組呢?下面具體來說對應的解決方案。具體來說,輔助平面的字元位共有2^20(即 2^4(輔助平面個數) x 2^16(每個輔助平面理論上可容納的字元個數))個,就是說,對應這些字元至少需要20個二進位制位。UTF-16將這20位拆成兩半,前10位對映在U+D800到U+DBFF(即56319 - 55296 = 2^10,空間大小2^10),稱為高位(H),後10位對映在U+DC00到U+DFFF(計算同前面的高位,空間大小2^10),稱為低位(L)。這意味著,一個輔助平面的字元,被拆成兩個基本平面的字元表示。當我們遇到兩個位元組,發現它的碼點在U+D800到U+DBFF之間,就可以斷定: 1.緊跟在後面的兩個位元組的碼點,應該在U+DC00到U+DFFF之間 2.這四個位元組必須放在一起解讀
由於歷史的原因,JavaScript使用UCS-2編碼,該編碼方案使用2個位元組表示一個字元。該編碼方案對於Unicode中大於2個位元組的字元處理帶了很多的侷限性,比如一個佔用4個位元組的字元,如果用使用UCS-2編碼方案處理,會被處理為兩個字元,這會導致字串的長度以及使用字元相關的函式,都無法得到正確的返回結果,要想得到正確的結果,必須使用polyfill部署自己的版本。
主要參考阮一峰的部落格http://www.ruanyifeng.com/blog/2014/12/unicode.html