字符串是一種數(shù)據(jù)類型炕置,但是朴摊,字符串比較特殊的是還有一個(gè)編碼問(wèn)題。
因?yàn)橛?jì)算機(jī)只能處理數(shù)字鹃操,如果要處理文本赴背,就必須先把文本轉(zhuǎn)換為數(shù)字才能處理耸三。
計(jì)算機(jī)中儲(chǔ)存的信息都是用二進(jìn)制數(shù)表示的仪壮;我們?cè)谄聊簧峡吹降挠⑽摹h字等字符是二進(jìn)制數(shù)轉(zhuǎn)換之后的結(jié)果缚陷。通俗的說(shuō),按照何種規(guī)則將字符存儲(chǔ)在計(jì)算機(jī)中虎锚,如'a'用什么表示非春,稱為"編碼"护侮;反之凳忙,將存儲(chǔ)在計(jì)算機(jī)中的二進(jìn)制數(shù)解析顯示出來(lái),稱為"解碼"柳恐,如同密碼學(xué)中的加密和解密。
ASCII:
每一個(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 碼往毡,一直沿用至今。比如大寫字母A的編碼是65开瞭,小寫字母z的編碼是122。
ASCII 碼一共規(guī)定了128個(gè)字符的編碼嗤详,比如空格SPACE是32(二進(jìn)制00100000),大寫的字母A是65(二進(jìn)制01000001)葱色。這128個(gè)符號(hào)(包括32個(gè)不能打印出來(lái)的控制符號(hào)),只占用了一個(gè)字節(jié)的后面7位苍狰,最前面的一位統(tǒng)一規(guī)定為0。
非 ASCII 編碼
英語(yǔ)用128個(gè)符號(hào)編碼就夠了淋昭,但是用來(lái)表示其他語(yǔ)言,128個(gè)符號(hào)是不夠的翔忽。比如盏檐,在法語(yǔ)中驶悟,字母上方有注音符號(hào)胡野,它就無(wú)法用 ASCII 碼表示痕鳍。于是,一些歐洲國(guó)家就決定笼呆,利用字節(jié)中閑置的最高位編入新的符號(hào)。比如抄邀,法語(yǔ)中的é的編碼為130(二進(jìn)制10000010)昼榛。這樣一來(lái)境肾,這些歐洲國(guó)家使用的編碼體系胆屿,可以表示最多256個(gè)符號(hào)。
但是非迹,這里又出現(xiàn)了新的問(wèn)題。不同的國(guó)家有不同的字母憎兽,因此,哪怕它們都使用256個(gè)符號(hào)的編碼方式纯命,代表的字母卻不一樣。比如亿汞,130在法語(yǔ)編碼中代表了é,在希伯來(lái)語(yǔ)編碼中卻代表了字母Gimel (?)疗我,在俄語(yǔ)編碼中又會(huì)代表另一個(gè)符號(hào)。但是不管怎樣吴裤,所有這些編碼方式中,0--127表示的符號(hào)是一樣的嚼摩,不一樣的只是128--255的這一段矿瘦。
至于亞洲國(guó)家的文字,使用的符號(hào)就更多了缚去,漢字就多達(dá)10萬(wàn)左右。一個(gè)字節(jié)只能表示256種符號(hào)易结,肯定是不夠的,就必須使用多個(gè)字節(jié)表達(dá)一個(gè)符號(hào)搞动,而且還不能和ASCII編碼沖突,所以鹦肿,中國(guó)制定了GB2312編碼,用來(lái)把中文編進(jìn)去箩溃。(GB2312采用2個(gè)字節(jié),理論上最多可以表示 256 x 256 = 65536 個(gè)符號(hào))
Unicode
你可以想得到的是涣旨,全世界有上百種語(yǔ)言,日本把日文編到Shift_JIS里霹陡,韓國(guó)把韓文編到Euc-kr里。要想打開(kāi)一個(gè)文本文件烹棉,就必須知道它的編碼方式,否則用錯(cuò)誤的編碼方式解讀峦耘,就會(huì)出現(xiàn)亂碼。另外旅薄,各國(guó)有各國(guó)的標(biāo)準(zhǔn),就會(huì)不可避免地出現(xiàn)沖突洛口,在多語(yǔ)言混合的文本中,顯示出來(lái)會(huì)有亂碼第焰。
如果有一種編碼,將世界上所有的符號(hào)都納入其中妨马。每一個(gè)符號(hào)都給予一個(gè)獨(dú)一無(wú)二的編碼杀赢,那么亂碼問(wèn)題就會(huì)消失湘纵。這就是 Unicode脂崔,就像它的名字都表示的梧喷,這是一種所有符號(hào)的編碼。
Unicode標(biāo)準(zhǔn)也在不斷發(fā)展铺敌,但最常用的是用兩個(gè)字節(jié)表示一個(gè)字符(如果要用到非常偏僻的字符,就需要4個(gè)字節(jié))〕テ荆現(xiàn)代操作系統(tǒng)和大多數(shù)編程語(yǔ)言都直接支持Unicode。
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 只是一個(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é)聚霜,甚至更多珠叔。
這里就有兩個(gè)嚴(yán)重的問(wèn)題:
- 如何才能區(qū)別 Unicode 和 ASCII ?計(jì)算機(jī)怎么知道三個(gè)字節(jié)表示一個(gè)符號(hào)祷安,而不是分別表示三個(gè)符號(hào)呢?
- 第二個(gè)問(wèn)題是汇鞭,我們已經(jīng)知道,英文字母只用一個(gè)字節(jié)表示就夠了霍骄,如果 Unicode 統(tǒng)一規(guī)定,每個(gè)符號(hào)用三個(gè)或四個(gè)字節(jié)表示读整,那么每個(gè)英文字母前都必然有二到三個(gè)字節(jié)是0,這對(duì)于存儲(chǔ)來(lái)說(shuō)是極大的浪費(fèi)米间,文本文件的大小會(huì)因此大出二三倍,這是無(wú)法接受的屈糊。
它們?cè)斐傻慕Y(jié)果是:1)出現(xiàn)了 Unicode 的多種存儲(chǔ)方式,也就是說(shuō)有許多種不同的二進(jìn)制格式逻锐,可以用來(lái)表示 Unicode。2)Unicode 在很長(zhǎng)一段時(shí)間內(nèi)無(wú)法推廣昧诱,直到互聯(lián)網(wǎng)的出現(xiàn)。
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 碼是相同的;(即趟章,ASCII編碼實(shí)際上可以被看成是UTF-8編碼的一部分,所以,大量只支持ASCII編碼的歷史遺留軟件可以在UTF-8編碼下繼續(xù)工作宏侍。)
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 碼。
跟據(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)始踩叭,依次從后向前填入格式中的,多出的位補(bǔ)
容贝。
這樣就得到了,“嚴(yán)”的 UTF-8 編碼是
?
?
斤富,
轉(zhuǎn)換成十六進(jìn)制就是E4B8A5。
Unicode 與 UTF-8 之間的轉(zhuǎn)換
里面有四個(gè)選項(xiàng):ANSI满力,Unicode,Unicode big endian和UTF-8
1)ANSI是默認(rèn)的編碼方式:對(duì)于英文文件是ASCII編碼油额,對(duì)于簡(jiǎn)體中文文件是GB2312編碼(只針對(duì) Windows 簡(jiǎn)體中文版,如果是繁體中文版會(huì)采用 Big5 碼)潦嘶;
2)Unicode編碼這里指的是notepad.exe使用的 UCS-2 編碼方式:即直接用兩個(gè)字節(jié)存入字符的 Unicode 碼,這個(gè)選項(xiàng)用的 little endian 格式;
3)Unicode big endian編碼與上一個(gè)選項(xiàng)相對(duì)應(yīng):我在下一節(jié)會(huì)解釋 little endian和 big endian 的涵義校摩;
4)UTF-8編碼:也就是上一節(jié)談到的編碼方法。
選擇完"編碼方式"后衙吩,點(diǎn)擊"保存"按鈕,文件的編碼方式就立刻轉(zhuǎn)換好了坤塞。
Little endian 和 Big endian
上一節(jié)已經(jīng)提到,UCS-2 格式可以存儲(chǔ) Unicode 碼(碼點(diǎn)不超過(guò)0xFFFF)摹芙。以漢字嚴(yán)為例,Unicode 碼是4E25浮禾,需要用兩個(gè)字節(jié)存儲(chǔ),一個(gè)字節(jié)是4E盈电,另一個(gè)字節(jié)是25。存儲(chǔ)的時(shí)候匆帚,4E在前,25在后吸重,這就是 Big endian 方式;25在前嚎幸,4E在后,這是 Little endian 方式嫉晶。
這兩個(gè)古怪的名稱來(lái)自英國(guó)作家斯威夫特的《格列佛游記》。在該書中,小人國(guó)里爆發(fā)了內(nèi)戰(zhàn)封断,戰(zhàn)爭(zhēng)起因是人們爭(zhēng)論,吃雞蛋時(shí)究竟是從大頭(Big-endian)敲開(kāi)還是從小頭(Little-endian)敲開(kāi)坡疼。為了這件事情,前后爆發(fā)了六次戰(zhàn)爭(zhēng),一個(gè)皇帝送了命闸氮,另一個(gè)皇帝丟了王位。
第一個(gè)字節(jié)在前蒲跨,就是"大頭方式"(Big endian),第二個(gè)字節(jié)在前就是"小頭方式"(Little endian)或悲。
那么很自然的,就會(huì)出現(xiàn)一個(gè)問(wèn)題:計(jì)算機(jī)怎么知道某一個(gè)文件到底采用哪一種方式編碼巡语?
Unicode 規(guī)范定義,每一個(gè)文件的最前面分別加入一個(gè)表示編碼順序的字符男公,這個(gè)字符的名字叫做"零寬度非換行空格"(zero width no-break space),用FEFF表示枢赔。這正好是兩個(gè)字節(jié),而且FF比FE大1糠爬。
如果一個(gè)文本文件的頭兩個(gè)字節(jié)是FE FF,就表示該文件采用大頭方式执隧;如果頭兩個(gè)字節(jié)是FF FE,就表示該文件采用小頭方式镀琉。
10、實(shí)例講解
下面屋摔,舉一個(gè)實(shí)例。
打開(kāi)"記事本"程序notepad.exe钓试,新建一個(gè)文本文件,內(nèi)容就是一個(gè)“嚴(yán)”字弓熏,依次采用ANSI,Unicode挽鞠,Unicode big endian和UTF-8編碼方式保存狈孔。
1)ANSI:文件的編碼就是兩個(gè)字節(jié)D1 CF材义,這正是嚴(yán)的 GB2312 編碼均抽,這也暗示 GB2312 是采用大頭方式存儲(chǔ)的其掂。
2)Unicode:編碼是四個(gè)字節(jié)FF FE 25 4E,其中FF FE表明是小頭方式存儲(chǔ)清寇,真正的編碼是4E25。
3)Unicode big endian:編碼是四個(gè)字節(jié)FE FF 4E 25华烟,其中FE FF表明是大頭方式存儲(chǔ)。
4)UTF-8:編碼是六個(gè)字節(jié)EF BB BF E4 B8 A5盔夜,前三個(gè)字節(jié)EF BB BF表示這是UTF-8編碼,后三個(gè)E4B8A5就是嚴(yán)的具體編碼喂链,它的存儲(chǔ)順序與編碼順序是一致的。
總結(jié):
簡(jiǎn)單來(lái)說(shuō):Unicode椭微、GBK和Big5碼等就是編碼的值(也就是術(shù)語(yǔ)“字符集”),而UTF-8蝇率、UTF-16、UTF32之類就是這個(gè)值的表現(xiàn)形式(即術(shù)語(yǔ)“編碼格式”)本慕。
另外:Unicode、GBK和Big5碼等字符集是不兼容的锅尘,同一個(gè)漢字在這三個(gè)字符集里的碼值是完全不一樣的。如"漢"的Unicode值與GBK就是不一樣的藤违,假設(shè)Unicode為a040,GBK為b030顿乒。以UTF-8為例,UTF-8碼完全只針對(duì)Unicode來(lái)組織的淆游,如果GBK要轉(zhuǎn)UTF-8必須先轉(zhuǎn)Unicode碼,再轉(zhuǎn)UTF-8就OK了犹菱。
即GBK、GB2312等與UTF8之間都必須通過(guò)Unicode編碼才能相互轉(zhuǎn)換:
1)GBK腊脱、GB2312 --先轉(zhuǎn)--> Unicode --再轉(zhuǎn)--> UTF8
2)UTF8 --先轉(zhuǎn)--> Unicode --再轉(zhuǎn)--> GBK、GB2312
參考文獻(xiàn):
Unicode,UTF-8,ASCII等字符編碼
字符編碼那點(diǎn)事:快速理解ASCII陕凹、Unicode、GBK和UTF-8