Unicode的來歷
提到編碼,首先要說的是ASCII碼
ASCII碼
在學校學 C 語言的時候学密,了解到一些計算機內(nèi)部的機制淘衙,知道所有的信息最終都表示為一個二進制的字符串,每一個二進制位有 0 和 1 兩種狀態(tài)腻暮,通過不同的排列組合彤守,使用 0 和 1 就可以表示世界上所有的東西,感覺有點中國“太極”的感覺——“太極生兩儀哭靖,兩儀生四象具垫,四象生八卦”。
在計算機種中试幽,1 字節(jié)對應 8 位二進制數(shù)筝蚕,而每位二進制數(shù)有 0、1 兩種狀態(tài)抡草,因此 1 字節(jié)可以組合出 256 種狀態(tài)饰及。如果這 256 中狀態(tài)每一個都對應一個符號,就能通過 1 字節(jié)的數(shù)據(jù)表示 256 個字符康震。美國人于是就制定了一套編碼(其實就是個字典
)燎含,描述英語中的字符和這 8 位二進制數(shù)的對應關系,這被稱為 ASCII 碼腿短。
ASCII 碼一共定義了 128 個字符屏箍,例如大寫的字母 A 是 65(這是十進制數(shù),對應二進制是0100 0001)橘忱。這 128 個字符只使用了 8 位二進制數(shù)中的后面 7 位赴魁,最前面的一位統(tǒng)一規(guī)定為 0。
歷史問題
英語用 128 個字符來編碼完全是足夠的钝诚,但是用來表示其他語言颖御,128 個字符是遠遠不夠的。于是凝颇,一些歐洲的國家就決定潘拱,將 ASCII 碼中閑置的最高位利用起來疹鳄,這樣一來就能表示 256 個字符。但是芦岂,這里又有了一個問題瘪弓,那就是不同的國家的字符集可能不同,就算它們都能用 256 個字符表示全禽最,但是同一個碼
點(也就是 8 位二進制數(shù))表示的字符可能可能不同腺怯。例如,144 在阿拉伯人的 ASCII 碼中是 ?川无,而在俄羅斯的 ASCII 碼中是 ?呛占。
因此,ASCII 碼的問題在于盡管所有人都在 0 - 127 號字符上達成了一致舀透,但對于 128 - 255 號字符上卻有很多種不同的解釋栓票。與此同時,亞洲語言有更多的字符需要被存儲愕够,一個字節(jié)已經(jīng)不夠用了走贪。于是,人們開始使用兩個字節(jié)來存儲字符惑芭。
各種各樣的編碼方式成了系統(tǒng)開發(fā)者的噩夢坠狡,因為他們想把軟件賣到國外。于是遂跟,他們提出了一個“內(nèi)碼表”的概念逃沿,可以切換到相應語言的一個內(nèi)碼表,這樣才能顯示相應語言的字母幻锁。在這種情況下凯亮,如果使用多語種,那么就需要頻繁的在內(nèi)碼表內(nèi)進行切換哄尔。
綜上可以總結(jié)為假消,需要一部足夠大的字典來標識所有的字符。最終岭接,美國人意識到他們應該提出一種標準方案來展示世界上所有語言中的所有字符富拗,出于這個目的,Unicode誕生了鸣戴。
Unicode是什么
Unicode 當然是一本很厚的字典啃沪,記錄著世界上所有字符對應的一個數(shù)字(code point
)。具體是怎樣的對應關系窄锅,又或者說是如何進行劃分的创千,就不是我們考慮的問題了,我們只用知道 Unicode 給所有的字符指定了一個數(shù)字用來表示該字符。
它僅僅只是一個字符集签餐,規(guī)定了符合對應的二進制代碼寓涨,至于這個二進制代碼如何存儲則沒有任何規(guī)定。它的想法很簡單氯檐,就是為每個字符規(guī)定一個 用來表示該字符的數(shù)字,僅此而已体捏。
Unicode 是一本很厚的字典冠摄,她將全世界所有的字符定義在一個集合里。這么多的字符不是一次性定義的几缭,而是分區(qū)定義河泳。每個區(qū)可以存放 65536 個(216216)字符,稱為一個平面(plane)年栓。目前拆挥,一共有 17 個(2^5)平面,也就是說某抓,整個 Unicode 字符集的大小現(xiàn)在是2^21,當然實際沒有這么多纸兔。
最前面的 65536 個字符位,稱為基本平面(簡稱 BMP )否副,它的碼點范圍是從 0 到 2^16?1汉矿,寫成 16 進制就是從 U+0000 到 U+FFFF。所有最常見的字符都放在這個平面备禀,這是 Unicode 最先定義和公布的一個平面洲拇。剩下的字符都放在輔助平面(簡稱 SMP ),碼點范圍從 U+010000 到 U+10FFFF
所以unicode的code point范圍用十六進制標識為:
0x0000 0000 -- 0x0010 FFFF
Unicode 編碼
為了較好的解決 Unicode 的編碼問題曲尸, UTF-8 和 UTF-16 兩種當前比較流行的編碼方式誕生了赋续。當然還有一個 UTF-32 的編碼方式,也就是上述那種定長編碼另患,字符統(tǒng)一使用 4 個字節(jié)纽乱,雖然看似方便,但是卻不如另外兩種編碼方式使用廣泛柴淘。
UTF-8編碼
UTF-8 是一個非常驚艷的編碼方式迫淹,漂亮的實現(xiàn)了對 ASCII 碼的向后兼容,以保證 Unicode 可以被大眾接受为严。
UTF-8 是目前互聯(lián)網(wǎng)上使用最廣泛的一種 Unicode 編碼方式敛熬,它的最大特點就是可變長。它可以使用 1 - 4 個字節(jié)表示一個字符第股,根據(jù)字符的不同變換長度应民。編碼規(guī)則如下:
1、對于單個字節(jié)的字符,第一位設為 0诲锹,后面的 7 位對應這個字符的 Unicode 碼點繁仁。因此,對于英文中的 0 - 127 號字符归园,與 ASCII 碼完全相同黄虱。這意味著 ASCII 碼那個年代的文檔用 UTF-8 編碼打開完全沒有問題。
2庸诱、對于需要使用 N 個字節(jié)來表示的字符(N > 1)捻浦,第一個字節(jié)的前 N 位都設為 1,第 N + 1 位設為0桥爽,剩余的 N - 1 個字節(jié)的前兩位都設位 10朱灿,剩下的二進制位則使用這個字符的 Unicode 碼點來填充。
十六進制表示編碼范圍 | UTF-8二進制表示 | 可以填充的二進制位數(shù) |
---|---|---|
0000 0000--0000 007F | 0XXXXXXX | 7 |
0000 0080--0000 07FF | 110XXXXX 10XXXXXX | 11 |
0000 0800--0000 FFFF | 1110XXXX 10XXXXXX 10XXXXXX | 16 |
0001 0000--0010 FFFF | 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX | 21 |
根據(jù)上面編碼規(guī)則對照表钠四,進行 UTF-8 編碼和解碼就簡單多了盗扒。下面以漢字“漢”為利,具體說明如何進行 UTF-8 編碼和解碼缀去。
“漢”的 Unicode 碼點是 0x6c49(110 1100 0100 1001)侣灶,通過上面的對照表可以發(fā)現(xiàn),0x0000 6c49 位于第三行的范圍朵耕,那么得出其格式為 1110xxxx 10xxxxxx 10xxxxxx
炫隶。接著,從“漢”的二進制數(shù)最后一位開始阎曹,從后向前依次填充對應格式中的 x伪阶,多出的 x 用 0 補上。這樣处嫌,就得到了“漢”的 UTF-8 編碼為 11100110 10110001 10001001
栅贴,轉(zhuǎn)換成十六進制就是 0xE6 0xB1 0x89
。
解碼的過程也十分簡單:如果一個字節(jié)的第一位是 0 熏迹,則說明這個字節(jié)對應一個字符檐薯;如果一個字節(jié)的第一位1,那么連續(xù)有多少個 1注暗,就表示該字符占用多少個字節(jié)
UTF-16編碼
UTF-16 編碼介于 UTF-32 與 UTF-8 之間坛缕,同時結(jié)合了定長和變長兩種編碼方法的特點。它的編碼規(guī)則很簡單:基本平面的字符占用 2 個字節(jié)捆昏,輔助平面的字符占用 4 個字節(jié)赚楚。也就是說,UTF-16 的編碼長度要么是 2 個字節(jié)(U+0000 到 U+FFFF)骗卜,要么是 4 個字節(jié)(U+010000 到 U+10FFFF)宠页。那么問題來了左胞,當我們遇到兩個字節(jié)時,到底是把這兩個字節(jié)當作一個字符還是與后面的兩個字節(jié)一起當作一個字符呢举户?
這里有一個很巧妙的地方烤宙,在基本平面內(nèi),從 U+D800 到 U+DFFF
是一個空段俭嘁,即這些碼點不對應任何字符躺枕。因此,這個空段可以用來映射輔助平面的字符兄淫。
輔助平面的字符位共有 2^20 (2^4個平面屯远,每個里面 2^16)個,因此表示這些字符至少需要 20 個二進制位捕虽。UTF-16 將這 20 個二進制位分成兩半,前 10 位映射在 U+D800 到 U+DBFF
坡脐,稱為高位(H)泄私,后 10 位映射在 U+DC00 到 U+DFFF,稱為低位(L)备闲。這意味著晌端,一個輔助平面的字符,被拆成兩個基本平面的字符表示恬砂。
因此咧纠,當我們遇到兩個字節(jié),發(fā)現(xiàn)它的碼點在 U+D800 到 U+DBFF 之間泻骤,就可以斷定漆羔,緊跟在后面的兩個字節(jié)的碼點,應該在 U+DC00 到 U+DFFF 之間狱掂,這四個字節(jié)必須放在一起解讀演痒。