什么是Unicode字符集?簡(jiǎn)單地說,它就是把全世界人類發(fā)明和使用的現(xiàn)有的所有字符進(jìn)行了集中收集和逐一編碼,這個(gè)過程就像把上學(xué)時(shí)老師把班里學(xué)生都叫到一起,統(tǒng)計(jì)總數(shù)后給每個(gè)學(xué)生分配一個(gè)唯一的學(xué)號(hào)一樣。Unicode字符集里收錄的字符可以是文字(如:‘α’、‘魍’等),也可以是符號(hào)(如:‘@’、‘$’),還可以是圖形(如'?'等)。
那它有什么用呢?它有兩個(gè)重要的用途:
一是解決了人們和機(jī)器之間的字符交互問題。每個(gè)字符不再是一個(gè)個(gè)抽象的文字、符號(hào)或圖形,而是變成了一個(gè)個(gè)的數(shù)字,每個(gè)數(shù)字對(duì)應(yīng)一個(gè)唯一的字符,而每個(gè)字符也有一個(gè)唯一的數(shù)字,兩者之間是一一對(duì)應(yīng)關(guān)系,而且不同字符和不同數(shù)字都各不相同,避免了“重名重姓”問題。這里,提到的表示字符的數(shù)字,我們也稱之為碼點(diǎn),后面我們還會(huì)詳細(xì)介紹。
二是解決了不用語(yǔ)言國(guó)家字符集編碼不統(tǒng)一的問題,提供了一個(gè)統(tǒng)一的編碼方式,避免“各自為政,政出多門”的問題,方便相互之間的數(shù)據(jù)交流。
有了基本概念,那么我們看看Unicode字符集是如何實(shí)現(xiàn)對(duì)所有字符編碼的。根據(jù)官網(wǎng)公布的Unicode 最新版本(9.0)介紹,Unicode字符集現(xiàn)在共包括128,172 個(gè)字符,可查看http://www.unicode.org/versions/Unicode9.0.0/ 。如此大量的字符,該如何編碼?最笨的辦法就是把所有字符列出來(lái),然后一個(gè)一個(gè)編個(gè)號(hào),但這樣不利于查找,也不利于分類,更不利于進(jìn)行存儲(chǔ)空間優(yōu)化編碼(后面會(huì)介紹一些優(yōu)化編碼方案)。
那Unicode字符集怎么解決這個(gè)編碼問題呢?它采用的是“分塊編碼”。按照國(guó)籍、地區(qū)、用途、功能等不同屬性,把字符先進(jìn)行分類,然后再根據(jù)每個(gè)小字符類的字符個(gè)數(shù),確定一個(gè)個(gè)大小不同的碼塊,下面節(jié)選了幾種字符及其對(duì)應(yīng)的碼點(diǎn)。
(節(jié)選)
0000..007F; Basic Latin(基本拉丁字母)
4E00..9FFF; CJK Unified Ideographs(CJK統(tǒng)一表意文字)
1D100..1D1FF; Musical Symbols(音樂符號(hào))
100000..10FFFF; Supplementary Private Use Area-B(補(bǔ)充專用區(qū)域-B)
注意,“0000”、“007F”、“1D100”以及“100000”等,都是十六進(jìn)制,這是每個(gè)字符在Unicode字符集中的編號(hào),也就是相當(dāng)于每個(gè)字符的“學(xué)號(hào)”。
可以看出,要表示一個(gè)字符,最長(zhǎng)需要6位十六進(jìn)制數(shù),換算一下就是24位二進(jìn)制數(shù);而短的,比如基本拉丁字母,前面的“0”省去,只要2位十六進(jìn)制(8位二進(jìn)制數(shù))就行了。
有了字符集,下面就要談?wù)勅绾伪硎竞褪褂眠@些字符(碼點(diǎn))了。畢竟,誰(shuí)也不會(huì)閑了沒事把字符編個(gè)號(hào)就為了練自己認(rèn)字和數(shù)數(shù)的能力。最重要的當(dāng)然是為了讓不同信息受體間交換信息。
于是,就出現(xiàn)了UTF。所謂UTF是Unicode Transformation Format的縮寫,意為Unicode轉(zhuǎn)換格式。UTF具體分為3類,分別是UTF-32,UTF-16和UTF-8。
先看UTF-32。UTF-32是定長(zhǎng)編碼,也就是說每個(gè)字符的編碼長(zhǎng)度都是固定的,‘32‘是其所使用的二進(jìn)制編碼的位數(shù),即:32位。但通常以字節(jié)數(shù)進(jìn)行量化,所以32位對(duì)應(yīng)的字節(jié)數(shù)為4字節(jié)。
我們的Unicode字符集每個(gè)字符的碼點(diǎn)最長(zhǎng)也就是24位,相當(dāng)于3個(gè)字節(jié),而UTF-32給了4個(gè)字節(jié)(32位)來(lái)表示,給了字符集非常大的擴(kuò)展空間(有興趣的童鞋可以算算32位二進(jìn)制數(shù)最大可以表示多大的數(shù),這個(gè)數(shù)基本就對(duì)應(yīng)了可以表示多少字符)。
沒這時(shí)間計(jì)算這些的童鞋你就簡(jiǎn)單理解為,UTF-32就是一個(gè)“運(yùn)超大箱”的快遞公司,不管你寄什么,它都統(tǒng)一拿裝冰箱的盒子寄(覺得不夠大的,自行腦補(bǔ)一個(gè)),保證能一次裝下你要寄的東西。
它的優(yōu)點(diǎn)是被表示的Unicode字符都是固定長(zhǎng)度的,易于查找和解碼;但缺點(diǎn)是表示常用字符時(shí)內(nèi)存占用太大,本地存儲(chǔ)利用率或傳輸效率太低。
UTF-16是變長(zhǎng)編碼,也就是說每個(gè)字符的編碼長(zhǎng)度是變化的,不是一成不變的。它的編碼算法為:
假設(shè)字符的碼點(diǎn)為c,c的形式為:“XXXXXX”,c對(duì)應(yīng)著Unicode字符的碼點(diǎn),它即它的范圍從“000000”至“10FFFF”。
- 若000000< c < 00FFFF,則UTF-16的編碼結(jié)果就是碼點(diǎn)c去掉前面8位“0”的后16位表示;
- 若010000< c < 10FFFF,則:
首先,c減去“010000”(十六進(jìn)制),則c將從“XXXXXX”6位十六進(jìn)制碼降為“XXXXX”十六進(jìn)制碼(如:10FFFF - 010000 = 0FFFFF);
其次,由于1位十六進(jìn)制碼對(duì)應(yīng)4位二進(jìn)制碼,則上述十六進(jìn)制碼可換算成20位二進(jìn)制碼
最后,以10位為一組,分別加上6位標(biāo)識(shí)符,則可得到32位,即4字節(jié)二進(jìn)制碼,如下式所示:
110110bbbbbbbbbb 110111bbbbbbbbbb
式中,粗體字為另外加上的識(shí)別碼,用于與“000000-00FFFF”編碼后的單個(gè)2字節(jié)數(shù)進(jìn)行區(qū)分;‘b’表示任意二進(jìn)制數(shù)。
在UTF-16中,2字節(jié)是字符的基本表示單元,低碼點(diǎn)的用2字節(jié)表示,高碼點(diǎn)的拆開后用2個(gè)2字節(jié)表示。
還是拿快遞公司的例子類比,UTF-16是家提供了一種“運(yùn)中等箱子”的快遞公司,中等箱子能裝下的就直接寄,裝不下的,做個(gè)標(biāo)記,分兩個(gè)箱子寄,收件人需要特別注意下標(biāo)記,如果沒有標(biāo)記,直接就用,如果有,就把兩個(gè)箱子?xùn)|西取出來(lái)拼起來(lái)后再用。
UTF-8也是變長(zhǎng)編碼,它的編碼算法與UTF-16并無(wú)本質(zhì)區(qū)別,都是對(duì)Unicode進(jìn)行分段,然后加上標(biāo)識(shí)碼,唯一的區(qū)別是分段更多。其算法如下:
- 字符碼點(diǎn)在000000至00007F,由于最大值“7F”可用“111 1111” 7位二進(jìn)制碼表示,故編碼為1個(gè)字節(jié),即:
0bbbbbb- 字符碼點(diǎn)在000080至0007FF,由于最大值“7FF”可用“111 1111 1111” 11位二進(jìn)制碼表示,故編碼為2個(gè)字節(jié),即:
110bbbbb 10bbbbbb (有效位為前5后6)
其中,標(biāo)識(shí)位為110和10;‘b’表示任意二進(jìn)制數(shù)。- 字符碼點(diǎn)在000800至00FFFF,由于最大值“FFFF”可用“1111 1111 1111 1111” 16位二進(jìn)制碼表示,故編碼為3個(gè)字節(jié),即:
1110bbbb 10bbbbbb 10bbbbbb (有效位為前4中6后6)
其中,標(biāo)識(shí)位為1110、10和10;‘b’表示任意二進(jìn)制數(shù)。- 字符碼點(diǎn)在010000至10FFFF,由于最大值“10FFFF”可用“1 0000 1111 1111 1111 1111” 21位二進(jìn)制碼表示,故編碼為4個(gè)字節(jié),即:
11110bbb 10bbbbbb 10bbbbbb 10bbbbbb (有效位為前3后全6)
其中,標(biāo)識(shí)位為11110、10、10和10;‘b’表示任意二進(jìn)制數(shù)。
在UTF-8中,1字節(jié)是字符的基本表示單元,最低的碼點(diǎn)(000000-0000FF)用1字節(jié)表示,高的碼點(diǎn)(000080-10FFFF)進(jìn)一步分段,分別拆開為2個(gè)、3個(gè)和4個(gè)1字節(jié)。
可見,相比較而言,UTF-8是家只能“運(yùn)小箱子”的快遞公司,少數(shù)能裝下的就用1個(gè)箱子運(yùn),不能裝下的就拿2個(gè)、3個(gè)甚至4個(gè)來(lái)運(yùn)。作為收件人,會(huì)非常辛苦的進(jìn)行逐一判別,基本上都是需要拆箱組裝后才能使用的(下面講到也會(huì)有特例)。
當(dāng)然,UTF-8在對(duì)于拉丁語(yǔ)系國(guó)家或者字符為主的信息傳遞和數(shù)據(jù)處理時(shí),效率是非常高的,因?yàn)閯偛臮nicode字符集節(jié)選中提到的基本拉丁語(yǔ)范圍剛好是0000..007F,在UTF-8中只要1個(gè)字節(jié)就夠了。但是,對(duì)于中日韓(CJK,China-Japan-Korea)語(yǔ)系或字符為主的信息傳遞和數(shù)據(jù)處理時(shí),效率就不那么好了,因?yàn)閯偛殴?jié)選的中日韓表意文字范圍是4E00..9FFF,那在UTF-8中進(jìn)行編解碼時(shí)必須按照上面UTF-8算法的第3條進(jìn)行處理,也就是要用3個(gè)字節(jié)來(lái)表示(還不如UTF-16的2字節(jié)),所以國(guó)內(nèi)很多中文數(shù)據(jù)較多的網(wǎng)站一般也不會(huì)采用UTF-8來(lái)進(jìn)行編碼,但作為程序猿還是比較喜歡用這種的編碼方式。
完。