0x01 首先了解一下ASCII孟抗、GBK 和?Unicode钻心、UTF-8字符編碼的那些事
?( 摘自網絡流傳甚廣的一個例子 )
很久很久以前扔役,有一群人,他們決定用8個可以開合的晶體管來組合成不同的狀態(tài)亿胸,以表示世界上的萬物。他們看到8個開關狀態(tài)是好的婉刀,于是他們把這稱為”字節(jié)“序仙。再后來,他們又做了一些可以處理這些字節(jié)的機器律秃,機器開動了治唤,可以用字節(jié)來組合出很多狀態(tài),狀態(tài)開始變來變去船惨。他們看到這樣是好的,于是它們就這機器稱為”計算機“疙挺。
開始計算機只在美國用怜浅。八位的字節(jié)一共可以組合出256(2的8次方)種不同的狀態(tài)。 他們把其中的編號從0開始的32種狀態(tài)分別規(guī)定了特殊的用途锦爵,一但終端奥裸、打印機遇上約定好的這些字節(jié)被傳過來時沪袭,就要做一些約定的動作。遇上0×10, 終端就換行侠鳄,遇上0×07, 終端就向人們嘟嘟叫死宣,例好遇上0x1b, 打印機就打印反白的字,或者終端就用彩色顯示字母博秫。他們看到這樣很好眶掌,于是就把這些0×20以下的字節(jié)狀態(tài)稱為”控制碼”。他們又把所有的空 格即寒、標點符號召噩、數字、大小寫字母分別用連續(xù)的字節(jié)狀態(tài)表示市咽,一直編到了第127號抵蚊,這樣計算機就可以用不同字節(jié)來存儲英語的文字了溯革。大家看到這樣谷醉,都感覺 很好,于是大家都把這個方案叫做ANSI的”Ascii”編碼(American Standard Code for Information Interchange抖单,美國信息互換標準代碼)遇八。當時世界上所有的計算機都用同樣的ASCII方案來保存英文文字刃永。
后來,就像建造巴比倫塔一樣斯够,世界各地的都開始使用計算機,但是很多國家用的不是英文抓督,他們的字母里有許多是ASCII里沒有的束亏,為了可以在計算機保存他們的文字,他們決定采用 127號之后的空位來表示這些新的字母定铜、符號雀久,還加入了很多畫表格時需要用下到的橫線赖捌、豎線、交叉等形狀越庇,一直把序號編到了最后一個狀態(tài)255卤唉。從128 到255這一頁的字符集被稱”擴展字符集“。從此之后桑驱,貪婪的人類再沒有新的狀態(tài)可以用了,美帝國主義可能沒有想到還有第三世界國家的人們也希望可以用到計算機吧痊硕!
等中國人們得到計算機時岔绸,已經沒有可以利用的字節(jié)狀態(tài)來表示漢字,況且有6000多個常用漢字需要保存呢盒揉。但是這難不倒智慧的中國人民刚盈,我們不客氣地把那些127號之后的奇異符號們直接取消掉, 規(guī)定:一個小于127的字符的意義與原來相同,但兩個大于127的字符連在一起時扁掸,就表示一個漢字谴分,前面的一個字節(jié)(他稱之為高字節(jié))從0xA1用到 0xF7镀脂,后面一個字節(jié)(低字節(jié))從0xA1到0xFE,這樣我們就可以組合出大約7000多個簡體漢字了沙兰。在這些編碼里翘魄,我們還把數學符號、羅馬希臘的字母斋射、日文的假名們都編進去了但荤,連在 ASCII 里本來就有的數字、標點桑包、字母都統(tǒng)統(tǒng)重新編了兩個字節(jié)長的編碼纺非,這就是常說的”全角”字符,而原來在127號以下的那些就叫”半角”字符了弱左。 中國人民看到這樣很不錯科贬,于是就把這種漢字方案叫做 “GB2312“。GB2312 是對 ASCII 的中文擴展榜掌。
但是中國的漢字太多了憎账,我們很快就就發(fā)現有許多人的人名沒有辦法在這里打出來,特別是某些很會麻煩別人的國家領導人邪意。于是我們不得不繼續(xù)把 GB2312 沒有用到的碼位找出來老實不客氣地用上反砌。 后來還是不夠用,于是干脆不再要求低字節(jié)一定是127號之后的內碼策菜,只要第一個字節(jié)是大于127就固定表示這是一個漢字的開始酒贬,不管后面跟的是不是擴展字符集里的內容。結果擴展之后的編碼方案被稱為GBK標準蠢莺,GBK包括了GB2312 的所有內容零如,同時又增加了近20000個新的漢字(包括繁體字)和符號。 后來少數民族也要用電腦了耸携,于是我們再擴展辕翰,又加了幾千個新的少數民族的字喜命,GBK擴成了GB18030河劝。從此之后矛紫,中華民族的文化就可以在計算機時代中傳承了。 中國的程序員們看到這一系列漢字編碼的標準是好的务甥,于是通稱他們叫做 “DBCS“(Double Byte Charecter Set 雙字節(jié)字符集)喳篇。在DBCS系列標準里麸澜,最大的特點是兩字節(jié)長的漢字字符和一字節(jié)長的英文字符并存于同一套編碼方案里,因此他們寫的程序為了支持中文處理编矾,必須要注意字串里的每一個字節(jié)的值馁害,如果這個值是大于127的,那么就認為一個雙字節(jié)字符集里的字符出現了裆操。那時候凡是受過加持炉媒,會編程的計算機僧侶 們都要每天念下面這個咒語數百遍: “一個漢字算兩個英文字符吊骤!一個漢字算兩個英文字符……”
因為當時各個國家都像中國這樣搞出一套自己的編碼標準静尼,結果互相之間誰也不懂誰的編碼,誰也不支持別人的編碼鸭巴,連大陸和臺灣這樣只相隔了150海里拦盹,使用著同一種語言的兄弟地區(qū),也分別采用了不同的 DBCS編碼方案——當時的中國人想讓電腦顯示漢字恬口,就必須裝上一個”漢字系統(tǒng)”,專門用來處理漢字的顯示歉秫、輸入的問題养铸,但是那個臺灣的愚昧封建人士寫的算命程序就必須加裝另一套支持 BIG5編碼的什么”倚天漢字系統(tǒng)”才可以用钞螟,裝錯了字符系統(tǒng),顯示就會亂了套裂明!這怎么辦太援?而且世界民族之林中還有那些一時用不上電腦的窮苦人民,他們的文字又怎么辦仙蛉? 真是計算機的巴比倫塔命題凹蠲伞赛惩!
正在這時,大天使加百列及時出現了——一個叫ISO(國際標誰化組織)的國際組織決定著手解決這個問題篮绰。他們采用的方法很簡單:廢了所有的地區(qū)性編碼方案吠各,重新搞一個包括了地球上所有文化、所有字母和符號 的編碼!他們打算叫它”Universal Multiple-Octet Coded Character Set”,簡稱UCS, 俗稱 “unicode“符匾。unicode開始制訂時垛贤,計算機的存儲器容量極大地發(fā)展了,空間再也不成為問題了。于是 ISO就直接規(guī)定必須用兩個字節(jié)炬守,也就是16位來統(tǒng)一表示所有的字符曹洽,對于ASCII里的那些“半角”字符,unicode包持其原編碼不變环凿,只是將其長度由原來的8位擴展為16位,而其他文化和語言的字符則全部重新統(tǒng)一編碼。由于”半角”英文符號只需要用到低8位,所以其高8位永遠是0董朝,因此這種大氣的方案在保存英文文本時會多浪費一倍的空間祟绊。
這時候扭弧,從舊社會里走過來的程序員開始發(fā)現一個奇怪的現象:他們的strlen函數靠不住了,一個漢字不再是相當于兩個字符了,而是一個!是的遵馆,從unicode開始换况,無論是半角的英文字母,還是全角的漢字亏栈,它們都是統(tǒng)一的”一個字符“!同時脐往,也都是統(tǒng)一的”兩個字節(jié)“,請注意”字符”和”字節(jié)”兩個術語的不同巷燥,“字節(jié)”是一個8位的物理存貯單元,而“字符”則是一個文化相關的符號抛姑。在unicode中拍屑,一個字符就是兩個字節(jié)僵驰。一個漢字算兩個英文字符的時代已經快過去了。
unicode同樣也不完美星爪,這里就有兩個的問題粉私,一個是诺核,如何才能區(qū)別unicode和ascii?計算機怎么知道三個字節(jié)表示一個符號漓摩,而不是分別表示三個符號呢入客?第二個問題是,我們已經知道夭咬,英文字母只用一個字節(jié)表示就夠了卓舵,如果unicode統(tǒng)一規(guī)定膀钠,每個符號用三個或四個字節(jié)表示,那么每個英文字母前都必然有二到三個字節(jié)是0忘巧,這對于存儲空間來說是極大的浪費睦刃,文本文件的大小會因此大出二三倍涩拙,這是難以接受的。
unicode在很長一段時間內無法推廣工育,直到互聯(lián)網的出現搓彻,為解決unicode如何在網絡上傳輸的問題,于是面向傳輸的眾多UTF(UCS Transfer Format)標準出現了搪泳,顧名思義扼脐,UTF-8就是每次8個位傳輸數據瓦侮,而UTF-16就是每次16個位。UTF-8就是在互聯(lián)網上使用最廣的一種unicode的實現方式肚吏,這是為傳輸而設計的編碼须喂,并使編碼無國界,這樣就可以顯示全世界上所有文化的字符了仔役。
UTF-8最大的一個特點是己,就是它是一種變長的編碼方式卒废。它可以使用1~4個字節(jié)表示一個符號,根據不同的符號而變化字節(jié)長度逆皮,當字符在ASCII碼的范圍時电谣,就用一個字節(jié)表示抹蚀,保留了ASCII字符一個字節(jié)的編碼做為它的一部分,注意的是unicode一個中文字符占2個字節(jié)环壤,而UTF-8一個中文字符占3個字節(jié))晒来。從unicode到uft-8并不是直接的對應,而是要過一些算法和規(guī)則來轉換郑现。
0x02 編碼總而言之概括為
計算機發(fā)明后湃崩,人們制定了一種編碼荧降,叫ASCII碼。ASCII碼由一個字節(jié)中的7位(bit)表示竹习,范圍是0x00 - 0x7F 共128個字符誊抛。如果需要按照表格方式打印這些字符的時候列牺,缺少了“制表符”整陌。于是又擴展了ASCII的定義瞎领,使用一個字節(jié)的全部8位(bit)來表示字符了泌辫,這就叫擴展ASCII碼。范圍是0x00 - 0xFF 共256個字符九默。
中國人利用連續(xù)2個擴展ASCII碼的擴展區(qū)域(0xA0以后)來表示一個漢字震放,該方法的標準叫GB-2312。后來驼修,日文殿遂、韓文、阿拉伯文乙各、臺灣繁體(BIG-5)......都使用類似的方法擴展了本地字符集的定義墨礁,現在統(tǒng)一稱為 MBCS 字符集(多字節(jié)字符集)。這個方法是有缺陷的耳峦,因為各個國家地區(qū)定義的字符集有交集恩静,因此使用GB-2312的軟件,就不能在BIG-5的環(huán)境下運行(顯示亂碼)蹲坷,反之亦然驶乾。
為了把全世界人民所有的所有的文字符號都統(tǒng)一進行編碼,于是制定了UNICODE標準字符集循签。UNICODE 使用2個字節(jié)表示一個字符级乐。這下終于好啦,全世界任何一個地區(qū)的軟件县匠,可以不用修改地就能在另一個地區(qū)運行了风科。雖然我用 IE 瀏覽日本網站,顯示出我不認識的日文文字聚唐,但至少不會是亂碼了丐重。UNICODE 的范圍是 0x0000 - 0xFFFF 共6萬多個字符,其中光漢字就占用了4萬多個
簡單來說杆查,unicode扮惦,gbk和BIG-5就是編碼的值,而utf-8,uft-16之類就是這個值的表現形式亲桦,同一個漢字崖蜜,那三個碼值是完全不一樣的.如"漢"的uncode值與gbk就是不一樣的浊仆,假設uncode為a040,gbk為b030豫领,而uft-8碼抡柿,就是把那個值表現的形式.utf-8碼完全只針對uncode來組織的,如果GBK要轉UTF-8必須先轉uncode碼等恐,再轉utf-8就號了.
0x03 關于中文編碼
為了處理漢字洲劣,程序員設計了用于簡體中文的GB2312和用于繁體中文的big5。
GB2312(1980年)一共收錄了7445個字符课蔬,包括6763個漢字和682個其它符號囱稽。漢字區(qū)的內碼范圍高字節(jié)從B0-F7,低字節(jié)從A1-FE二跋,占用的碼位是72*94=6768战惊。其中有5個空位是D7FA-D7FE。
GB2312支持的漢字太少扎即。1995年的漢字擴展規(guī)范GBK1.0收錄了21886個符號吞获,它分為漢字區(qū)和圖形符號區(qū)。漢字區(qū)包括21003個字符谚鄙。
從ASCII各拷、GB2312到GBK,這些編碼方法是向下兼容的襟锐,即同一個字符在這些方案中總是有相同的編碼撤逢,后面的標準支持更多的字符。在這些編碼中粮坞,英文和中文可以統(tǒng)一地處理蚊荣。區(qū)分中文編碼的方法是高字節(jié)的最高位不為0。按照程序員的稱呼莫杈,GB2312互例、GBK都屬于雙字節(jié)字符集?(DBCS)。
2000年的GB18030是取代GBK1.0的正式國家標準筝闹。該標準收錄了27484個漢字媳叨,同時還收錄了藏文、蒙文关顷、維吾爾文等主要的少數民族文字糊秆。從漢字字匯上說,GB18030在GB13000.1的20902個漢字的基礎上增加了CJK擴展A的6582個漢字(Unicode碼0x3400-0x4db5)议双,一共收錄了27484個漢字痘番。
utf-8中文字符占三個字節(jié),GB18030兼容GBK兼容GB2312中文字符占兩個字節(jié);有一些輸入只允許輸入英文數字等字符,可以通過字節(jié)數判斷utf-8是否有中文輸入