子曰:亂碼是一種思念,而思念是一種病。相信很多Web人常常糾結(jié)于亂碼當(dāng)中,可能是展現(xiàn)、可能是表單提交、可能是數(shù)據(jù)庫、可能是接口、可能是抓取……反正任何一個(gè)涉及到輸入或者輸出字符的地方都有可能被你碰到過亂碼。
為了解釋和解決亂碼問題,并且明確一些常見的誤區(qū),我打算寫這樣一個(gè)系列,介紹一些字符編碼相關(guān)的東西,目前規(guī)劃了3篇。
基本上內(nèi)容會(huì)比較偏科普性質(zhì),希望大神們輕拍,因?yàn)槲蚁嘈胚@些將會(huì)是非常重要的基礎(chǔ)知識,如果你能完整的讀下去,肯定會(huì)在你以后遇到亂碼的時(shí)候幫助到你的思考方式,快速找到原因。
我們的語言基本上都圍繞著字符,就是character,常簡稱char,很多時(shí)候字符會(huì)是文本的最小組成單位(注意只是“很多時(shí)候”,因?yàn)槭澜缡瞧婷畹模?/p>
不是一定要文字才叫字符,一些注音字符、數(shù)學(xué)符號、某些文字里的修飾符號、特殊符號、表格符號、甚至Emoji等等,其實(shí)都是字符。
字符要成套才有用,比如“英文字母”就是一個(gè)字符集,當(dāng)然這么說聽起來對計(jì)算機(jī)毫無意義。
一般我們所說的字符集(Character Set)就是一個(gè)規(guī)范,它收錄若干字符,并且給這些字符逐一分配了一個(gè)編號當(dāng)作索引(為了不過早引入編碼的概念造成混淆,這里我稱其為“編號”)。
ASCII是當(dāng)今計(jì)算機(jī)世界最經(jīng)典的字符集,它收錄了英文字母和若干標(biāo)點(diǎn),還有一些專門供計(jì)算機(jī)使用(不是給人看)的控制字符。
GB2312是一個(gè)常見的中文字符集,其中“GB”就是“國標(biāo)”(咱們國家很多不同行業(yè)的標(biāo)準(zhǔn)代號都是這樣命名的)。它收錄了大概幾千個(gè)漢字,以及幾百個(gè)西文字符。
GBK是微軟最早在Win95里實(shí)現(xiàn)的對GB2312進(jìn)行的擴(kuò)展,追加了很多繁體字和西文字符,總計(jì)收錄的字符數(shù)大概有20000多個(gè)。K就是拼音“擴(kuò)展”。
GB18030是國標(biāo)對GB2312的升級(當(dāng)然中間還有其他升級,但大多被淹沒在歷史潮流里面了),它一下就收錄了70000多個(gè)字符,最大的升級部分有繁體中文字符、新字、生僻字、少數(shù)民族文字、日韓字符等。
上面三個(gè)GB系列的字符集都是為中文設(shè)計(jì)的,當(dāng)然也擴(kuò)充了一些東亞語文的內(nèi)容(CJK字符,Chinese, Japanese, Korean),因?yàn)檫@些鄰居的字符在中國也挺常出現(xiàn)的。
又稱大五碼,是繁體中文地區(qū),例如灣灣、港港、澳澳常用的字符集,大概收錄了一萬多個(gè)字符,其中以繁體中文為主。因?yàn)楸籛indows所接受成為繁中版的默認(rèn)編碼而成為了事實(shí)標(biāo)準(zhǔn)。
就像中文一樣,幾乎每種語言都存在一個(gè)給自己的語言設(shè)計(jì)字符集的問題。
ISO意識到這個(gè)問題之后,設(shè)計(jì)了一套通用字符集UCS(Universal Character Set),目的是用一套字符集表示全世界(甚至外星人!?)的所有字符!
結(jié)果UCS成功了,因?yàn)榛ヂ?lián)網(wǎng)發(fā)展的太快了,任何國家的人每天都在互聯(lián)網(wǎng)上瀏覽來自全世界的不同語言不同文字的內(nèi)容,大家當(dāng)然希望用一套字符集就能收錄全世界所有的字符。
很多人會(huì)把字符集和字符編碼的概念搞混,其實(shí)不怪,因?yàn)檫@倆東西好多時(shí)候都是捆綁定義的。
字符編碼(Character Encoding)就是按照一定的技術(shù)要求(比如以8bit為單位)對字符集中的每一個(gè)字符進(jìn)行編碼,以便文本能夠在計(jì)算機(jī)和網(wǎng)絡(luò)傳輸上使用。
簡單的說,就是把字符集里面的那個(gè)每個(gè)字符的編號,給弄成計(jì)算機(jī)能懂的格式。
很多字符集在制定的時(shí)候,就已經(jīng)配套了它的編碼方案,比如ASCII、GB系列、Big5。對于這種字符集/編碼,稱呼上雖然模糊,但結(jié)合技術(shù)語境而言一般也不會(huì)有什么誤解。
標(biāo)準(zhǔn)ASCII只收錄了128個(gè)字符,使用7bit可以完美編碼。例如英文字母A
的ASCII編碼就是十六進(jìn)制0x41
,然后1字節(jié)剩下的一位就沒啥用了,可以用來當(dāng)奇偶校驗(yàn)。
后來ASCII被擴(kuò)展到了8bit,供256個(gè)字符,用8bit也就是1字節(jié)可以完美編碼,并且低7字節(jié)完全兼容。
ASCII是國際標(biāo)準(zhǔn)而擴(kuò)展ASCII并不是,下文所說到的“兼容ASCII”都是指兼容7bit的標(biāo)準(zhǔn)ASCII。
GB2312使用1/2字節(jié)變長編碼,單字節(jié)部分是兼容ASCII,其他幾千個(gè)字符都是用雙字節(jié)編碼。
GB2312在編碼的時(shí)候使用了一個(gè)“分區(qū)”的概念,小時(shí)候家里有一本區(qū)位碼表,就是配合Windows里古老的“區(qū)位輸入法”用的。
GBK的編碼方案是GB2312的超集,它完全兼容GB2312,不過把GB2312里面沒定義的那些編碼空間都用起來了。
GB18030的編碼方案稍微復(fù)雜一點(diǎn),它用的是1/2/4字節(jié)變長編碼方案。它完全兼容GB2312,基本上兼容GBK。
Big5使用固定兩字節(jié)編碼,它的首字節(jié)避開了ASCII的范圍,因此實(shí)際在程序?qū)崿F(xiàn)上面它可以近似兼容ASCII,由于它的低字節(jié)包含了一些ASCII字符,這個(gè)兼容也是不完美的,具體情況可以看看維基百科,非常有趣。
Unicode有一個(gè)非常高大上的中文名字叫萬國碼,呵呵,這個(gè)名字真是散發(fā)著一股農(nóng)業(yè)重金屬的氣息啊。其實(shí)也是個(gè)字符集,它和UCS之間有微妙的高度雷同關(guān)系,好在兩邊的組織都意識到了搞分化是不好的,于是互相之間達(dá)成了高度的一致。雖然它們的確是兩個(gè)不同的標(biāo)準(zhǔn),但很多時(shí)候混淆來看也無妨。
Unicode是定長編碼,根據(jù)版本不同,它有2字節(jié)(對應(yīng)UCS-2)、4字節(jié)(對應(yīng)UCS-4)的版本。
因?yàn)閁nicode是定長的,它實(shí)在太簡單粗暴了。例如如果用4字節(jié)的Unicode來傳輸英文文本就浪費(fèi)了3倍的體積,而用2字節(jié)的版本也不爽,一來容量較小,二來對于英文文本也還是浪費(fèi)的。于是在實(shí)現(xiàn)上對它進(jìn)行了一定的優(yōu)化,稱為Unicode轉(zhuǎn)換格式(Unicode Transformation Format)也就是我們耳熟能詳?shù)?strong>UTF了。
UTF-32是UCS-4的最樸素的實(shí)現(xiàn)方式,就是簡單地用定長4字節(jié)。
缺點(diǎn)嘛很明顯就是很浪費(fèi)體積。
優(yōu)點(diǎn)也是有的,首先就是把它轉(zhuǎn)換到Unicode最簡單,而且對于“第[i]個(gè)字符”這種隨機(jī)訪問也很好計(jì)算,直接字節(jié)數(shù)/4
就是了對不?
但因?yàn)榻M合字符(比如越南語,網(wǎng)上用來搞一個(gè)超長的流淚圖標(biāo)破壞排版那種)的存在,一個(gè)UTF-32碼元(4字節(jié))嚴(yán)格上也并非一個(gè)文本編輯上的單元,這種情況下對于排版系統(tǒng)而言UTF-32沒有太多優(yōu)勢。
UTF-16是使用2/4字節(jié)實(shí)現(xiàn)的UCS-4變長編碼。
因?yàn)榇蠖鄶?shù)時(shí)候用到的字符不超過65536個(gè),所以UTF-16在大多數(shù)時(shí)候1個(gè)字符都只占2字節(jié),這樣比起UTF-32省了接近一半體積,同時(shí)它的解析也不會(huì)太麻煩。
固定長的編碼方式對于計(jì)算機(jī)程序而言有一個(gè)非常大的優(yōu)勢就是字符串處理會(huì)容易的多,尤其是正則表達(dá)式的實(shí)現(xiàn)。因此很多現(xiàn)代語言,例如C#/Java的字符串內(nèi)部實(shí)現(xiàn)使用UTF-16,因?yàn)樗且环N效率和體積比較平衡的編碼方式。
UTF-8應(yīng)該是現(xiàn)在互聯(lián)網(wǎng)上使用最廣泛的統(tǒng)一語言編碼實(shí)現(xiàn)方式了。
它是1-4字節(jié)變長編碼(原本是1-6字節(jié),但是因?yàn)楹竺婺切┏隽薝nicode定義了,后來就改成1-4字節(jié)了)。單字節(jié)的情況兼容ASCII,在這個(gè)由英文主宰的互聯(lián)網(wǎng)環(huán)境里面這是非常好的特性,因?yàn)樗诤芏鄷r(shí)候會(huì)非常節(jié)省體積,而且這種時(shí)候完全不需要編碼轉(zhuǎn)換。
但它的缺點(diǎn)也相當(dāng)明顯,將UTF-8轉(zhuǎn)換到Unicode的算法會(huì)更加復(fù)雜,效率降低。
對于中文環(huán)境而言UTF-8也比較吃虧,因?yàn)槭褂肬TF-8編碼大多數(shù)中文字符需要3字節(jié),這就比GB系列和UTF-16浪費(fèi)空間。
UTF-8并未編碼0x10FFFF以上的部分,所以嚴(yán)格的說它只是UCS-4的子集。好在缺失的那部分本身就不受UCS/Unicode的重視,估計(jì)實(shí)在是太犄角旮旯了。
我覺得UTF-8最終成為互聯(lián)網(wǎng)主流很大一定程度是因?yàn)樗膯巫止?jié)是兼容ASCII的。
收錄了很多字符,并且編號,給人看的。
實(shí)現(xiàn)一個(gè)字符集,將它的編號以一定規(guī)則用二進(jìn)制實(shí)現(xiàn),給計(jì)算機(jī)看的。
中國的國標(biāo)字符集/編碼,GB2312和GBK已經(jīng)基本上過時(shí)了,如果要良好的支持中日韓文,又逃不開GBK的魔爪(比如歷史代碼束縛),那可以考慮升級到GB18030,這是國標(biāo)的最新版,也是最先進(jìn)的一版。
把全世界上百萬個(gè)你見過的或者你沒見過的字符全部收錄進(jìn)一套字符集,已經(jīng)被全世界接受成為了國際標(biāo)準(zhǔn)。
UCS/Unicode轉(zhuǎn)換格式,就是實(shí)用的編碼方案,用于計(jì)算機(jī)實(shí)現(xiàn)。
UCS/Unicode的一種折衷了處理效率和存儲(chǔ)空間的編碼實(shí)現(xiàn)方案,常被各種現(xiàn)代語言當(dāng)作字符串內(nèi)部編碼使用。
UCS/Unicode的一種傾向于節(jié)省空間的編碼實(shí)現(xiàn)方案,因?yàn)閷SCII兼容,對英文文本非常有利,成為了當(dāng)今互聯(lián)網(wǎng)的主流(甚至事實(shí)標(biāo)準(zhǔn))。
如果你的網(wǎng)站沒有任何歷史包袱,直接上UTF-8別商量!
如果你的網(wǎng)站有一些歷史包袱,商量商量還是上UTF-8吧,包袱的接口上轉(zhuǎn)換一下編碼。
呵呵呵呵,雖然文章的標(biāo)題叫做《編碼歪傳》,但其實(shí)上面的內(nèi)容一點(diǎn)也不歪嘛。
有觀眾看不下去了:“拜托,你講這些什么亂七八糟的理論知識我沒啥興趣啊,我想知道的其實(shí)只是為什么我的網(wǎng)頁會(huì)亂碼啊老濕!”
對于上面的問題我只想說四個(gè)字:請聯(lián)系我請看下集:《編碼歪傳——Web篇》