字符编码那点事儿
那時,天下人的口音、言語,都是一樣。他們往東邊遷移的時候,在示拿地遇見一片平原,就住在那里。他們彼此商量說:“來吧!我們要作磚,把磚燒透了。”他們就拿磚當石頭,又拿石漆當灰泥。他們說:“來吧!我們要建造一座城和一座塔,塔頂通天,為要傳揚我們的名,免得我們分散在全地上。”耶和華降臨,要看看世人所建造的城和塔。耶和華說:“看哪!他們成為一樣的人民,都是一樣的言語,如今既作起這事來,以后他們所要作的事,就沒有不成就的了。我們下去,在那里變亂他們的口音,使他們的言語彼此不通。”于是,耶和華使他們從那里分散在全地上;他們就停工不造那城了。因為耶和華在那里變亂天下人的言語,使眾人分散在全地上,所以那城名叫巴別。
——《創世記》?
字符、字符集、字符編碼
先說字符,字符大家都很清楚了,英文叫做character,通常的簡寫叫做char,字符加上一種字體(Font),就可以得到字型(Glyph),字型就是我們眼前所見之物了。?
字符用來在計算機領域表示語言和文字,世間有無窮多字符,如果溝通的雙方要互相明白,就必須使用相同的字符集。字符集會規定一種或者幾種用計算機字節表示字符的方法,這個方法就叫做字符編碼。?ASCII?
ASCII恐怕是最最著名的字符集了,至今為止還有人將ASCII誤認為是計算機中表示字符的方法的代稱,在任何編程系統中都會問"如何獲取某一字符的ASCII碼"。ASCII中共有127個字符,主要包含大小寫英文字母、標點和一些控制符號。ASCII既是字符集,又是字符編碼,ASCII的每個字符對應著0-127中的一個整數,這個整數稱為字符的ASCII碼。例如,字符'a'的ASCII碼是97。
ASCII在計算機發展初期是非常NB的,這個時期主要是英語國家在使用計算機,而對當時的少數非英語國家用戶而言,英語根本不成為門檻,ASCII編碼可以放在一個Byte之中,這通常是計算機最小的空間單位。
哦對了,ASCII后來被國際標準化組織搞成了ISO/IEC 646,算是有了正式名分。
ASCII兼容編碼?
因為ASCII對于英語之外的文字完全沒有考慮,?所以顯然沒法滿足全球人民日益增長的字符使用需要,而由于ASCII編碼被廣泛使用,所以又沒法完完全全地重新發明一套新的字符集和編碼方式。
注意到ASCII只占用了0-127,但是一個字節其實可以表示256個數字,于是128到255或者說-128到-1這些值被一些人開始盯上了。于是一些ASCII兼容編碼開始出現了,其中我朝人民最為熟悉的就是GB2312了,unicode的UTF8編碼方式也是ASCII兼容的,這些后文再詳細分說。??
ASCII兼容是向下兼容,即某種編碼方式不需要經過任何轉換接受任何以ASCII編碼的文本為合法的,并且字符與當做ASCII編碼時是完全一致的。
幾乎所有的ASCII兼容編碼方式都是變長編碼,因為每個ASCII字符只占一個字節。?
GB2312、GBK、GB18030
ASCII字符集里面不包括中文,那么顯然這對于我國人民的感情來說是難以接受的,于是黨和各級人民政府當然不會允許這種事情發生。在倪光南院士等人的關懷下,1980年具有獨立自主知識產權的GB2312橫空出世了。
1995年,在社會主義偉大旗幟下,有關部門組織一些專家對GB2312做了一些擴充,形成了GBK編碼,也就是GB13000標準。
2000年,為了滿足人民日益增長的字符使用需要,我國又制定了GB18030標準。
GB系列的編碼還有一個稱呼叫做區位碼。
ANSI?
關于ANSI這種說不上是編碼方式還是字符集的充滿狗血故事的概念,還真是不太好講。好吧首先ANSI的意思是American National Standards Institute,美國國家標準學會,嗯,但是很遺憾美國國家標準學會從來沒有制定過這么一個標準,也不可能制定出來(好吧很快你就可以知道為什么了)。
最初這個東西如何出現,現在已經不可考證,但是毫無疑問至今windows的記事本保存選項里面仍然有這么一個叫做ANSI的選項。為什么說ANSI其實并不是一種字符集,也不是一個編碼呢?因為實際上ANSI會隨著操作系統的區域和語言設置而變化!當你將非unicode默認語言選成中文時,這個選項保存出來的文件赫然就是GB2312!對的!我認為ANSI這個詞乃是米帝國主義亡我之心不死,妄圖將偉大社會主義字符編碼標準GB2312劃入米國之手啊!
好吧,認真地說, ANSI這個概念頗為腦殘,它的實際意思是遵循本地化設置的編碼方式,但是卻不倫不類地扯上了米國國家標準學會。?
Unicode
講字符編碼一定不能漏了Unicode了,現在Unicode確實不負uni之名,是最有機會達成大統一重造巴別塔的一個字符集。Unicode的最新版本是6.0。
Unicode是一個字符集而不是一個編碼方式,Unicode中每個字符對應一個code point(中譯為碼點,覺得不喜歡這個翻譯) 。code point是一個0x00000到0x10FFFF之間的正整數。因此Unicode的字符都被記為U+XXXX,XXXX就是code point的16進制表示了。
Unicode還把code point分成17個code plane,其中主要使用的是0,1,2,3四個平面,也就是U+0000-U+FFFF,U+10000-U+1FFFF,U+20000-U+2FFFF,U+30000-U+3FFFF這四個code point的集合。Unicode本身是抽象的,它發布的時候會帶一個參考字型但是實際上它把字型的決定權交給了字體,所以code point就是字符的唯一標識了。
UTF
unicode本身并非編碼方式,所以unicode需要編碼方式配合,常見的unicode編碼方式就是UTF。UTF-X的數字X就表示最少用幾個bit來表示一個字符。
常見的表示方式有UTF-8、UTF-16、UTF-32、UTF-7、UTF-24......
Unicode的?code plane共只有17個,所以UTF-32已經是定長編碼了,它能表示的范圍是0x00000000到0xFFFFFFFF,這顯然遠遠超過現在Unicode的code point范圍,所以短期內可以認為UTF-32是定長編碼。定長編碼的好處是,獲取一個字符串的長度可以直接根據它占用的內存計算出,所以UTF家族中只有UTF-32可以享受到了。
UTF-8是最常用的UTF編碼,特別地,它還是ASCII兼容的。
值得注意的是,這些編碼里面,UTF-16使用甚廣,因為unicode1.0發布的時候,?UTF-16還可以認為是定長編碼,于是一些倒霉孩子比如Java就把UTF-16當成自己的字符串在內存中存儲使用的編碼,于是搞出來Char類型其實并不是每個表示一個字符,而是表示一個UTF-16 code unit這樣的人間慘劇。
UTF-8對英文文本來說是非常合適的,但是中文每個文字要占三個字節,相比之下,UTF-16中的中文則是只占2個字節,所以當你的系統對存儲容量或者網絡流量敏感的時候,應該根據你的文本主要組成方式選擇UTF恰當的編碼。
大端和小端
對于UTF-16、UTF-32這種東西而言,還有一個很現實的問題,它們的code unit都是一個正整數,而計算機系統的最小單位是字節,字節表示正整數是有高位低位順序之分的,于是我們就有了大端小端的事情,相信用過window記事本的同學都有印象,里面有兩個選項unicode big endian和unicode,實際上這里unicode指的就是UTF-16編碼。?
BOM
BOM又叫做Byte Order Mark,起因是我們的unicode里面規定了一個神奇的字符零寬空格U+FEFF,這個字符好啊,它明明是個字符,還顯示不出來,于是一些程序員大爺想出了個nb的辦法,約定所有的UTF數據流,都先吐出一個U+FEFF來,反正又不會顯示出來,根據這個U+FEFF的表示方法,就可以判斷數據流所使用的UTF編碼了。比如說,收到三個字節EF BB BF的話,就可以斷定來的編碼是UTF-8了。
這個NB辦法后來也被推廣到其它編碼上(嗯,比如GB就不行,跟BOM沖突,打開記事本輸入"聯通",然后保存為ANSI,再打開看看),也漸漸有了稱呼叫做BOM。但是很遺憾,不少人不懂規矩,不會在數據源前面加BOM,于是天下間的文本,又被分為了有BOM和無BOM兩種......?
UCS
很多人說Unicode的標準是ISO16406,實際上這話不對,UCS才是。這中間有一段非常蛋疼的歷史,unicode和ucs是分別由兩個不同組織編寫和發布的不同標準,但是因為二者在很早些時候就達成共識,一直緊密合作,始終默契地保持兩份標準的一致,所以其實可以認為Unicode就是UCS了。
然而跟Unicode不同,UCS本身還規定了編碼方式,主要有兩種:UCS-2和UCS-4,這里的2和4表示最少用幾個byte來表示一個字符。UCS-4也是定長編碼,它跟UTF-32完全一致,所以二者只是理論上是兩種編碼而已。
值得注意的是,UCS-2與UTF-16是不完全一致的,UCS-2也是一種定長編碼,code point超出0xFFFF的字符怎么辦呢?UCS-2......它表示不了這些字符!
ISO-8859
實際上,制定了自己的編碼標準的不僅僅是我們偉大的天朝,歐州國家也制定了一些自己的編碼規范,它們都是針對特定地區的編碼,不同語言使用不同字符集,所以互相之間不能混排,簡單地說,就是跟GB2312一路貨色,這些東西也就是前面提到過的ANSI編碼了。
ISO-8859是一組規范,而不是一個規范,ISO-8859-1大家一定經常在各處見到,它表示西歐字符集。ISO-8859家族現在一共有15個,序號是從1排到16,除了12之外,關于為啥沒有12有些不同說法,又說原本是要給加入塞爾特語族的Latin-7,也有說是留給印度天體梵文的,總之就是因為一些奇怪的原因跳過去了。
很遺憾我朝大佬對于ISO毫無興趣(也許ISO9001是例外?),因此GB始終是GB,沒能加入ISO當中,不過GB基本可以認為是跟ISO-8859同一體系的一種編碼。
結語
對于字符和編碼的這點事,我自己蛋疼了很久,才漸漸理解了其中錯綜復雜的關系,這篇文章信息量不大,不過是個感性理解,希望對一些人會有幫助吧。?
from: http://www.cnblogs.com/winter-cn/archive/2012/01/27/2328512.html
總結
- 上一篇: 答与微博前端教主在吃饭时讨论到的一道微软
- 下一篇: 面试时算法题的解答思路