?????亂碼是我們在日常的工作中經(jīng)常遇到的問題滥搭,你可能從網(wǎng)上好不容易下載了一個炫酷的jQuery插件何什,但是卻在打開的時候譬巫,發(fā)現(xiàn)某幾個js文件都是類似“澶у0咖楣?閬?”這樣的怪異符號芦昔,其實這就是編碼和解碼不一致導(dǎo)致的诱贿,就好像我用英文給你寫了篇信,你不懂英文用中文去解析它咕缎,自然覺得他是亂碼瘪松。
?????本篇文章將會從計算機碼的歷史演變開始咸作,簡單介紹下主流的幾種編碼方式。希望對大家日后處理亂碼有所幫助宵睦。
一记罚、為何需要編碼
?????由于計算機只能識別0和1,所以我們所有得字符和數(shù)據(jù)都是轉(zhuǎn)換成二進制0和1的序列存放在計算機中的壳嚎,但是我們在如何區(qū)分他們上產(chǎn)生了問題桐智。就比如說,給一萬個二進制的序列烟馅,你能讀取出來其中哪些是字母哪些是數(shù)字么说庭?不是不可以,難度很大郑趁,完全違背了我們使用計算機的初衷了刊驴。于是,我們可以規(guī)定每個字符的二進制序列寡润,并把它存在計算機內(nèi)捆憎,當需要將二進制位轉(zhuǎn)換成我們能看懂的字符數(shù)據(jù)時,讓計算機去截取二進制位查找對應(yīng)的表梭纹,翻譯成我們看的懂的數(shù)據(jù)躲惰。這規(guī)定字符的二進制序列就是一種編碼行為,讓計算機翻譯就是一種解碼行為变抽。
二础拨、ASCll 編碼
?????我們都知道,計算機是美國人民發(fā)明的绍载,所以他們在設(shè)計計算機編碼的時候并沒有考慮到給別的國家人用(尤其是我們第三世界國家人民)诡宗。ASCll 碼全稱是 American Standard Code for Information,美國信息互換標準代碼击儡。由于美帝人民使用26個英文字母的各種組合進行交流了(沒有我們幾萬漢字那么多)塔沃,于是他們使用八位二進制來表示所有的字符,從0到32用于特殊用途曙痘,比如:遇上00x10輸出換行,遇上0x1b打印機工作等立肘。還有一些空格边坤,數(shù)字,字母等谅年,一直編號到127(0111 1111)茧痒,實際上他們只用了7個字節(jié)編完了所有的字符,因為最高位始終為0融蹂。
三旺订、ISO 8859-1/Windows-1252
?????對于美國來說ASCll碼足夠用了弄企,但是后來歐洲的一些國家也開始使用計算機,他們也為自己國家的語言進行擴充編碼区拳,于是他們將美國沒用完的那個字節(jié)的后一半用來編碼自己國家的語言拘领,于是從128到255都有了定義。這一段編碼我們叫做擴展字符集樱调。ISO 8859-1就是這樣的編碼標準约素,0-127依然保存美帝的編碼,128-255編碼了一些他們需要使用到的字符笆凌。因為ISO 8859-1編碼標準出現(xiàn)的比較早圣猎,而在后來又出現(xiàn)了一些比較中要的符號例如(歐元符號),這些符號并沒有被編入乞而,于是Windows-1252編碼擴充了ISO 8859-1編碼標準送悔,刪除了一些相對不常用的字符,替換了一些新的字符爪模∏菲。可以說Windows-1252是ISO 8859-1的替代品。
四呻右、GB2312
?????終于輪到我們偉大的中國人民跪妥,當我們能夠使用計算機的時候,那一個字節(jié)已經(jīng)被使用完了声滥。于是我們決定保留美國的編碼(ASCll 碼)眉撵,其余歐洲國家的編碼全部刪除,對于英文和西歐字符使用一個字節(jié)足夠用了落塑,對于我們漢字來說纽疟,需要使用兩個字節(jié)來表示。小于127的依然表示原來的字符(也就是該字節(jié)最高位為0)憾赁,當計算機遇到兩個大于127的字節(jié)時候(也就是兩個字節(jié)的最高位都是1)污朽,就一次性讀取兩個字節(jié),將它解碼成一個漢字龙考。這就是GB2312編碼蟆肆,它大概能夠表示7000多個簡體漢字。不包括一些繁體字晦款,但是對于日常使用已經(jīng)足夠炎功。在這兩個字節(jié)中,高位字節(jié)表示范圍:0xA1-0xF7缓溅,低字節(jié)為表述范圍:0xA1-0xFE蛇损。(可能大家看出來,有些范圍并沒有定義編碼,后面說原因)
五淤齐、GBK
?????雖然已經(jīng)編碼了7000多個漢字股囊,足夠日常的使用了,但是我們勤勞的中國人民還是覺得不夠用更啄,于是他們發(fā)現(xiàn):一個漢字使用兩個字節(jié)表示稚疹,那如果第一個字節(jié)的最高位為1,那就不用將后一個字節(jié)的最高位也置為1锈死,直接往后讀取兩個字節(jié)就好了贫堰。于是我們又擴充了一半的漢字。高位字節(jié)表述范圍:0x81-0xFE待牵,低位字節(jié)表述范圍:0x40-0x7E和0x80-0xFE其屏。
六、GB18030
?????為了照顧日韓和我們的少數(shù)民族缨该,我們又對GBK加以擴充偎行,使用變長編碼,要么使用兩個字節(jié)表示要么用四個字節(jié)表示贰拿。(至于為什么三個字節(jié)不用來表示蛤袒,我也費解)那么我們怎么才能判斷出某個字符他是用幾個字節(jié)來表示的呢?用兩個字節(jié)表示的字符和GBK一樣膨更,用四個字節(jié)表示的字符妙真,第一個字節(jié)表述范圍:0x81-0xFE,第二個字節(jié)表述的范圍:0x30-0x39荚守,第三個字節(jié)表述的范圍:0x81-0xFE珍德,第四個字節(jié)表述范圍:0x30-0x39。每次解析的時候先拿過來這個字符的第二個字節(jié)判斷范圍是否在0x30-0x39之間矗漾,如果在說明這是四個字節(jié)表示的字符锈候,如果不在說明這是兩個字節(jié)表示的字符。從GBK的第二個字節(jié)表述范圍看敞贡,它是大于0x39的泵琳。(這就是它低位字節(jié)0x30-0x39不編碼的原因)
七、Unicode
?????每個國家都按照自己的標準編碼的字符集誊役,但是這會導(dǎo)致一個問題获列,兩個編碼不一致的國家的人相互之間交流成了很大的問題。(除了美國可以和任意的國家無障礙交流蛔垢,因為每個編碼標準都是兼容ASCll 的)击孩,于是當人們需要和別的國家之間進行交流的時候,就會在自己的系統(tǒng)上裝上一個對方國家的編碼轉(zhuǎn)換系統(tǒng)啦桌。十分麻煩溯壶。于是 一個叫ISO(國際標誰化組織)的組織打算對世界上的所有編碼進行統(tǒng)一,廢除所有地方性編碼方案甫男。這就是UNICODE且改。所以,準確上來說UNICODE并不算是一種具體的編碼標準板驳,它只是將世界上所有的字符進行的編號又跛,并沒有指定他們具體在計算機中以什么樣的形式存儲。
?????不像上述的各種編碼標準若治,準確的規(guī)定了每個字符在計算機中的二進制位慨蓝,而UNICODE只是將所有的字符進行了編號,具體怎么存儲它不關(guān)心端幼。于是它有了幾個具體的實現(xiàn):UTF-8礼烈,UTF-32,UTF-16等婆跑。UNICODE的編號范圍為:0x000000-0x10FFFF此熬,包括將近110多萬』基本囊括所有的字符犀忱,但是經(jīng)常使用的范圍在:0x0000-0xFFFF之間也就是65536以內(nèi)。
UTF-32
?????UTF-32編碼標準用固定4個字節(jié)進行編碼扶关。每個字符會有一個對應(yīng)的Unicode編號阴汇,這個編號是個整數(shù),它的二進制就是這個字符UTF-32編碼节槐。四個字節(jié)足夠表示世界上的所有的字符搀庶,但是對于只需要的一個字節(jié)的ASCll 編碼的字符也是使用了四個字節(jié)(浪費了三個字節(jié)),所以這種編碼標準唯一的缺點就是浪費疯淫。一個概念地来,"大端"和"小端"。大端表示的是:四個字節(jié)序列熙掺,高字節(jié)在前未斑,低字節(jié)在后。(也就是計算機讀取順序從前到后)币绩,小端則相反蜡秽,將低字節(jié)排列在前面。這種編碼方式缺點是很明顯的缆镣,就是浪費空間芽突,但是簡單。
UTF-16
?????UTF-16使用的是變長字節(jié)表示董瞻,相對復(fù)雜些寞蚌。分別使用兩個字節(jié)或者四個字節(jié)表示田巴。編號在 0x0000-0xFFFF之間的常用字符使用兩個字節(jié)表示,其中0xD800-0xDBFF之間的編號沒有定義字符挟秤。(用于辨別兩個字節(jié)還是四個字節(jié)和GB18030類似)對于編號在0x10000-0x10FFFF之間的字符使用四個字節(jié)表示壹哺。只不過前兩個字節(jié)被叫做高代理項后兩個字節(jié)被稱為低代理項,其中高代理項表述范圍:0xD800-0xDBFF正好是上面沒有定義的編號范圍艘刚,低代理項表述范圍:0xDC00-0xDFFF管宵。區(qū)分一個字符是用的兩個字節(jié)還是用的四個字節(jié)就不言而喻了。直接判斷前兩個字節(jié)的范圍即可攀甚。
?????這種編碼標準相對于UTF-32來說箩朴,是節(jié)約了不少空間,但是對于美國和西歐他們來說還是有點浪費秋度。這種編碼一般用于系統(tǒng)編碼炸庞。
UTF-8
?????為了充分利用資源,還是被我們智慧的人類發(fā)明了UTF-8荚斯。UTF-8使用變長字節(jié)表示燕雁,分別可以使用1到4個字節(jié)不等,對于Unicode編號越小的自然使用的字節(jié)就越小鲸拥。
?????可以看到編號在127以內(nèi)的使用一個字節(jié)表示(也就是ASCll )拐格,(128-2047)使用兩個字節(jié)表示,(2048-65535)使用三個字節(jié)表示刑赶,也就是我們大部分的漢字都是使用三個字節(jié)表示的捏浊,(65536以上)使用四個字節(jié)表示。
?????不同的區(qū)間范圍對應(yīng)了不同的模板撞叨。對于一個字符拿到Unicode編號之后金踪,轉(zhuǎn)換成而二進制判斷所屬范圍,使用模板編碼(具體怎么編碼馬上說)牵敷。除了用ASCll碼的字符們胡岔,其他字符的模板的第一個字節(jié)都n個1加一個0,表示什么意思呢枷餐?第一個字節(jié)有幾個1表示此字符由幾個字節(jié)表示靶瘸,方便計算機讀取,低位字節(jié)開頭都是10毛肋。 下面看一個例子:漢字“馬”的Unicode編號是0x9A6C怨咪,轉(zhuǎn)換成整數(shù)是39532,對應(yīng)的模板是:1110xxxx 10xxxxxx 10xxxxxx润匙。將整數(shù)39532轉(zhuǎn)換成二進制:1001 1010 0110 1100诗眨。將這個二進制位從右向左開始,一次填入模板中x中孕讳,得到如下結(jié)果:1110 1001 1010 1001 1010 1100 1110匠楚。這就是“馬”字的UTF-8編碼巍膘。計算機解碼的時候會一次性讀取三個字節(jié),逆操作解碼芋簿,查表顯示漢字典徘。
?????最后小結(jié)一下,對于之前的一些編碼標準都是按照編號轉(zhuǎn)為二進制來形成編碼益咬,GB18030使用變長字節(jié)表示,通過比較任意字符的第二個字節(jié)的范圍來判斷存儲時使用的字節(jié)數(shù)帜平。UTF-16也是一種變長字節(jié)表示方案幽告,通過比較高代理項的范圍來確定使用字節(jié)數(shù)。UTF-8編碼方式可以說是將上面的兩種編碼方式的有點擴充到了極致裆甩,使用變長字節(jié)表示冗锁,但是通過使用模板的方式來區(qū)分1-4個字節(jié)長度。只是過程有點復(fù)雜嗤栓,但是綜合還是UTF-8更加令人喜歡冻河。
?????個人覺得所有編碼標準應(yīng)該都廢棄,保留Unicode編碼方式茉帅,使得從整個世界的角度上所有編碼是統(tǒng)一的叨叙,這樣就會大大減少亂碼出現(xiàn)的頻率。實際上堪澎,蘋果早就拋棄所有方言編碼標準擂错,只接受Unicode編碼標準。
?????本篇文章是作者學(xué)習(xí)總結(jié)樱蛤,愚見钮呀,望對大家有幫助藻三。