Unicode 與 UTF-8 之間的轉(zhuǎn)換

JS 中,所有的數(shù)字都是用64位浮點(diǎn)數(shù)的形式存儲(chǔ)的。
我們知道數(shù)字是如何存儲(chǔ)的,十進(jìn)制轉(zhuǎn)成二進(jìn)制就可以了。那么字符是如何存儲(chǔ)的呢?轉(zhuǎn)成數(shù)字不就得了,再用二進(jìn)制存,但是'1'不能用1表示,那給字符編號(hào)不就好了嗎。
計(jì)算機(jī)還沒(méi)有普遍的時(shí)候,用 0~127 表示所有的符號(hào):(ASCII)
ASCII 碼
我們知道,計(jì)算機(jī)內(nèi)部,所有信息最終都是一個(gè)二進(jìn)制值。每一個(gè)二進(jìn)制位(bit)有0和1兩種狀態(tài),因此八個(gè)二進(jìn)制位就可以組合出256種狀態(tài),這被稱為一個(gè)字節(jié)(byte)。也就是說(shuō),一個(gè)字節(jié)一共可以用來(lái)表示256種不同的狀態(tài),每一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)符號(hào),就是256個(gè)符號(hào),從00000000到11111111。
上個(gè)世紀(jì)60年代,美國(guó)制定了一套字符編碼,對(duì)英語(yǔ)字符與二進(jìn)制位之間的關(guān)系,做了統(tǒng)一規(guī)定。這被稱為 ASCII 碼,一直沿用至今。
ASCII 碼使用指定的7 位或8 位二進(jìn)制數(shù)組合來(lái)表示128 或256 種可能的字符。標(biāo)準(zhǔn)ASCII 碼也叫基礎(chǔ)ASCII碼,使用7 位二進(jìn)制數(shù)(剩下的1位二進(jìn)制為0)來(lái)表示所有的大寫和小寫字母,數(shù)字0 到9、標(biāo)點(diǎn)符號(hào),以及在美式英語(yǔ)中使用的特殊控制字符。
后來(lái),中國(guó)人開(kāi)始使用電腦了,怎么辦呢,怎么表示中文呢?簡(jiǎn)單,還是編號(hào)。
中國(guó)國(guó)家標(biāo)準(zhǔn)局來(lái)編,名稱為國(guó)標(biāo)2312(GB2312),用十六進(jìn)制的 0000~FFFF 表示漢字。
一個(gè)十六進(jìn)制就是4個(gè)0/1位,F(xiàn)FFF就是16位,也就是兩個(gè)字節(jié)。最多收錄2^16=65536個(gè)字符。
后來(lái)發(fā)現(xiàn)中國(guó)人的名字里有很多的生僻字,這些字并沒(méi)有在GB2312中,那怎么表示這些生僻字、繁體字呢?這個(gè)時(shí)候,微軟出手了,微軟推出了一個(gè)國(guó)際擴(kuò)展,簡(jiǎn)稱GBK。GBK含21886個(gè)漢字和圖形符號(hào),收錄了中日韓使用的幾乎所有漢字,完全兼容GB2312,依然使用的是16位(兩字節(jié))。
后來(lái)發(fā)現(xiàn)網(wǎng)頁(yè)里還有藏文、泰文,怎么辦,顯示不了,簡(jiǎn)單,繼續(xù)編號(hào)。但是這一次,一次性解決全世界的需求。
Unicode
正如上一節(jié)所說(shuō),世界上存在著多種編碼方式,同一個(gè)二進(jìn)制數(shù)字可以被解釋成不同的符號(hào)。因此,要想打開(kāi)一個(gè)文本文件,就必須知道它的編碼方式,否則用錯(cuò)誤的編碼方式解讀,就會(huì)出現(xiàn)亂碼。為什么電子郵件常常出現(xiàn)亂碼?就是因?yàn)榘l(fā)信人和收信人使用的編碼方式不一樣。
可以想象,如果有一種編碼,將世界上所有的符號(hào)都納入其中。每一個(gè)符號(hào)都給予一個(gè)獨(dú)一無(wú)二的編碼,那么亂碼問(wèn)題就會(huì)消失。這就是 Unicode,就像它的名字都表示的,這是一種所有符號(hào)的編碼。
Unicode 當(dāng)然是一個(gè)很大的集合,現(xiàn)在的規(guī)??梢匀菁{100多萬(wàn)個(gè)符號(hào)。每個(gè)符號(hào)的編碼都不一樣,比如,U+0639表示阿拉伯字母Ain,U+0041表示英語(yǔ)的大寫字母A,U+4E25表示漢字嚴(yán)。具體的符號(hào)對(duì)應(yīng)表,可以查詢unicode.org,或者專門的漢字對(duì)應(yīng)表。
Unicode 的問(wèn)題
需要注意的是,Unicode 只是一個(gè)符號(hào)集,它只規(guī)定了符號(hào)的二進(jìn)制代碼,卻沒(méi)有規(guī)定這個(gè)二進(jìn)制代碼應(yīng)該如何存儲(chǔ)。
比如,漢字嚴(yán)的 Unicode 是十六進(jìn)制數(shù)4E25,轉(zhuǎn)換成二進(jìn)制數(shù)足足有15位(100111000100101),也就是說(shuō),這個(gè)符號(hào)的表示至少需要2個(gè)字節(jié)。表示其他更大的符號(hào),可能需要3個(gè)字節(jié)或者4個(gè)字節(jié),甚至更多。
UTF-8
互聯(lián)網(wǎng)的普及,強(qiáng)烈要求出現(xiàn)一種統(tǒng)一的編碼方式。UTF-8 就是在互聯(lián)網(wǎng)上使用最廣的一種 Unicode 的實(shí)現(xiàn)方式。其他實(shí)現(xiàn)方式還包括 UTF-16(字符用兩個(gè)字節(jié)或四個(gè)字節(jié)表示)和 UTF-32(字符用四個(gè)字節(jié)表示),不過(guò)在互聯(lián)網(wǎng)上基本不用。重復(fù)一遍,這里的關(guān)系是,UTF-8 是 Unicode 的實(shí)現(xiàn)方式之一。
UTF-8 最大的一個(gè)特點(diǎn),就是它是一種變長(zhǎng)的編碼方式。它可以使用1~4個(gè)字節(jié)表示一個(gè)符號(hào),根據(jù)不同的符號(hào)而變化字節(jié)長(zhǎng)度。
UTF-8 的編碼規(guī)則很簡(jiǎn)單,只有二條:
1)對(duì)于單字節(jié)的符號(hào),字節(jié)的第一位設(shè)為0,后面7位為這個(gè)符號(hào)的 Unicode 碼。因此對(duì)于英語(yǔ)字母,UTF-8 編碼和 ASCII 碼是相同的。
2)對(duì)于n字節(jié)的符號(hào)(n > 1),第一個(gè)字節(jié)的前n位都設(shè)為1,第n + 1位設(shè)為0,后面字節(jié)的前兩位一律設(shè)為10。剩下的沒(méi)有提及的二進(jìn)制位,全部為這個(gè)符號(hào)的 Unicode 碼。
下表總結(jié)了編碼規(guī)則,字母x表示可用編碼的位。
| UTF-8編碼方式| (二進(jìn)制)----------------------+---------------------------------------------0000 0000-0000 007F | 0xxxxxxx0000 0080-0000 07FF | 110xxxxx 10xxxxxx0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
跟據(jù)上表,解讀 UTF-8 編碼非常簡(jiǎn)單。如果一個(gè)字節(jié)的第一位是0,則這個(gè)字節(jié)單獨(dú)就是一個(gè)字符;如果第一位是1,則連續(xù)有多少個(gè)1,就表示當(dāng)前字符占用多少個(gè)字節(jié)。
下面,還是以漢字嚴(yán)為例,演示如何實(shí)現(xiàn) UTF-8 編碼。
嚴(yán)的 Unicode 是4E25(100111000100101),根據(jù)上表,可以發(fā)現(xiàn)4E25處在第三行的范圍內(nèi)(0000 0800 - 0000 FFFF),因此嚴(yán)的 UTF-8 編碼需要三個(gè)字節(jié),即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,從嚴(yán)的最后一個(gè)二進(jìn)制位開(kāi)始,依次從后向前填入格式中的x,多出的位補(bǔ)0。這樣就得到了,嚴(yán)的 UTF-8 編碼是11100100 10111000 10100101,轉(zhuǎn)換成十六進(jìn)制就是E4B8A5。
