字符編碼的前世今生

古代的通訊方式

很久很久,人們之間的的長途通訊主要是用信鴿、騎馬送報,烽煙等方式進行 : 



世界的第一條電報

直到1837年世界第一條電報誕生,當時美國科學家嘗試 用一些 "點"和 "劃"來表示不同的字母、數字和標點符號,這套表示的方是被稱為 "摩爾斯電碼" : 


世界第一台計算機

來到了1946年世界第一台計算機誕生。發明計算機的同學們用8個晶體管的 "通" 或 "斷" 組合出一些狀態表示世間萬物,不過當時的計算機有1.5間教室那麼大,六頭大象重,從現在來看來這簡直是個怪物,但在當時卻是震驚世界與改變世界的一項重要發明

ASCII

8個晶體館的 "通" 或 "斷"即可以代表一個字節,剛開始,計算機只在美國使用,所有的信息在計算機最底層都是以二進制("0" 或 "1" 兩種不同的狀態)的方式儲存,而8位的字節一共可以组合出256(2的8次方)種狀態,即256個字符,這對於當時的美國已經是足夠的了,她們嘗試把一些動作、字母、數字和符號用8位(bit)来组合 (主要包括控制字符(回車,退格,換行健); 可顯示大小寫字符 阿拉伯數字和西歐符):
  • 00000000 (0) ~00011111(31)  33 種用来表示终端的特殊動作
  • 00100000 (32) ~ 00101111(47) 、 00111010(58)~01100000(96) 和 01111101(125) ~ 01111110(126) 33 種表示英式標點符號
  • 00110000 (48) ~ 00111001 (57) 10 種來表示 "0~9" 10個阿拉伯数字
  • 01000001 (65) ~ 01011010(90) 和 01100001(97) ~ 01111010(122) 52種來表示大小寫英文字母
一共只用到了128種狀態,使用7位 (bits)表示一個字符, 但是7位編碼 的字符集只能支持到128個字符。共包括33個控制字符和95個可顯示字符,這一字符集被稱為ASCII(American Standard Code for Information Interchange,美国信息交换標準代碼,這一套字符集在1967年被正式公佈,主要顯示現代英語。

缺點:

ASCII最大的缺點就是只能顯示26個基本拉丁字母,數字,和標點符號,因此只能用於現代英語,而EASII雖然解決了部分西歐語言的顯示問題,對於更多國家的語言還適無能為力,因為蘋果最後拋棄了ASCII而轉用 Unicode

說到這裡,出了基礎概念及名詞:
  • 位元(bit):是計算機信息中的最小單位,是 binary digit(二進制数位) 的縮寫,指二進制中的一位
  • 字節(Byte):計算機中信息計量的一種单位,一個位就代表"0"或"1",每8個位(bit)组成一个字節(Byte)
  • 字符(Character):文字與符號的總稱,可以是各個國家的文字、標點符号、圖形符号、数字等
  • 字符集(Character Set):是多個字符的集合
  • 編碼(Encoding): 信息從一種形式或格式轉換为另一種形式的過程
  • 解碼(decoding): 編碼的逆過程
  • 字符編碼(Character Encoding): 按照何種规则存儲字符


EASCII

雖然剛開始計算機只在美國使用,128個字符的確是足夠了,但隨著科技驚人的發展,歐洲國家也開始使用上計算機了。不過128個字符明顯不夠,比如語法中,字母上方有注音符號 ,於是一些歐洲國家就決定,利用字節中閒置的最高位编入新的符号。比如,法語的é的二進制流為1000 0010,這樣一来,這些歐洲国家的编码體系,可以表示最多256个字符了。 但是,這裡又出现了新的问题。不同的國家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母卻不一样。比如,1000 0010在法语编码中代表了é,在希伯来语编码中卻代表了字母Gimel (?),在俄语编码中又会代表另一个符号。但是不管怎樣,所有这些编码方式中,0--127表示的符號是一樣的,不一樣的只是128--255的這一段。 EASCIIExtended ASCII,延伸美国標準信息交换码)由此而生EASCII碼比ASCII碼擴充出来的符号包括表格符号、計算符號、希臘字母和特殊的拉丁符号:

GB2312

EASCII碼對於部分歐洲国家機本夠用了,但不久之後,計算機到了中國,要知道漢字是世界上包含符號最多並且也是最難學的文字。 據不完全统计,漢字共包含了古文、現代文字等近10萬個文字,就是我们现在日常用的漢字也有幾千個,那麼對於只包含256個字符的EASCII 碼也難以满足這樣的需求了。 於是中國國家標準總局⌋(現已更名為⌈國家標準化管理委員會⌋)1981年,正式制訂了中華人民共和國國家標準簡體中文字符集,全稱信息交換用漢字编码字符集·基本集》,项目代號為GB 2312 或 GB 2312-80(GB為國際漢語拼音的首字母),此套字符集於當年的5月1日起正式實施。

包含字符:

共包含7445個字符,6763個漢字和682個其他字符(拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母)

儲存方式:

基於EUC儲存方式,每個漢字及符号以兩个字節来表示,第一個字節為“高位字節”,第二個字節為“低位字節”

BIG5

要知道港澳台同胞使用的是繁體字,而中国大陸制定的GB2312編碼並不包含繁体字,於是信息工業策進會在1984年與台湾13家廠商簽訂 "16位個人電腦套裝軟件合作開發(BIG-5)計算”,並開始編寫並推出BIG5標準。 之後推出的倚天中文系统 则屬於BIG5碼,並在台湾地區取得了巨大的成功。在BIG5诞生后,大部分的電腦軟件都使用了Big5碼,BIG5對於以台湾為核心的亞洲繁体漢字圈產生了久遠的影響,以至於後来的window 繁体中文版系统在台湾地區也以BIG5碼進行開發。Big5又稱為大五碼或五大碼,是使用繁體中文 社區中最常用的電腦漢字符集標準。中文碼分為內碼和交換碼兩類,Big5屬於中文內碼

包含字符:

共收錄13,060個漢字及441個符號

編碼方式:

使用了雙八碼儲存方法 ,兩個字節来為每個字符编码,第一個字節稱為“高位元字節"使用了0X81-0XEE,第二個字節稱為“低位元字節” 使用了0X40-0X7E






Unicode的崛起






Unicode

誕生:

在計算機進入中國大陸的相同時期,計算機也迅速發展並進入了世界各個國家。 特别是對於亞洲國家而言,每個國家都有自己的文字,於是每個國家或地區都像中國大陸這樣去制訂了自己的編碼標準,以便能在計算機上正確顯示自己國家的符號。 但帶来的结果就是國家之間誰也不懂别人的編碼,誰也不支持别人的編碼,連大陸和台湾這樣只相隔了150海里,都使用了不同的編碼體系。 於是,世界相關組織意識到了這個問題,並開始嘗試制定統一的編碼標準,以便能夠收納世界所有國家的文字編號。 在前期有兩個嘗試這一工作的組織:
  • 國際標準化組織(ISO)
  • 統一碼聯盟
國際標準化組織(ISO)及國際電工委員會(IEC)於1984年聯合成立了ISO/IEC小组,主要用於開發统一編碼項目; 而Xerox、Apple等軟件製造商則於1988年组成了统一碼聯盟,用於開發统一碼项目。 兩個組織都在編寫统一字符集,但後来他門發現各自在做同樣的事,同時世界上也不需要兩個不兼容的字符集,於是兩個組織就合併雙方的工作成果,並為創立一個單一编碼表而共同工作。
1991年,兩個組織共同的工作成果Unicode 1.0正式發布,不過Unicode 1.0並不包含CJK字符(即中日韓)。

Unicode是一個很大的集合,現在的規模可以容納100多萬個符號,每個符號的編碼都不一樣,U+0639表示阿拉伯字母Ain,U+0041表示英語的大寫字母A。具體的符號對應表,可以查詢Unicode.org。












Unicode的問題

需要注意的,Unicode只是一個符號集,他只規範了符號的二進制代碼,卻沒有規定這個二進制代碼該如何儲存,漢字"嚴"Unicode是16進制數4E25,轉換成二進制數足足有15位(100111000100101),也就是說這個符號表示至少需要2個字節,表示其他更大的符號,可能需要3個字節或是更多。

這裡有兩個嚴重的問題,第一個問題是,如呵才能區別Unucide和ASCII? 計算機怎麼知道三個字節表示一個符號而不是分別表示三個符號呢?第二個問題是,我門已經知道,英文字只用一個字節表示就足夠,那麼英文字母前都必然有2到3個字節是0,對於儲存來說是很大的浪費。

最後出現了 Unicode的多種儲存方式,也就是許多種不同的二進制格式。所以有很長的時間上無法推廣,直到互聯網的出現。

ISO/IEC 8859

ISO/IEC小组在1984年成立後的第三年(即1987年開始啟動ISO 8859標準的編寫,ISO 8859是一系列8位字符集的標準,主要为世界各地的不同語言(除CJK)而單獨編寫的字符集,一共定義了15個字符集:
  • ISO/IEC 8859-1:西欧语言
  • ISO/IEC 8859-2  :中欧语言
  • ISO/IEC 8859-3 :南欧语言
  • ISO/IEC 8859-4: 北欧语言
  • ISO/IEC 8859-5: 斯拉夫语
  • ISO/IEC 8859-6: 阿拉伯语
  • ISO/IEC 8859-7:希腊语
  • ISO/IEC 8859-8:希伯来语
  • ISO/IEC 8859-9:土耳其语
  • ISO/IEC 8859-10: 北日耳曼语
  • ISO/IEC 8859-11:泰语
  • ISO/IEC 8859-13: 波罗的语族
  • ISO/IEC 8859-14: 凯尔特语族
  • ISO/IEC 8859-15:西欧语言,收录芬兰语字母和大写法语重音字母,以及欧元(€)符号
  • ISO/IEC 8859-16 :东南欧语言,主要供罗马尼亚语使用,并加入欧元(€)符号
其中ISO/IEC 8859-1至ISO/IEC 8859-4四個项目早在1982年就已經編寫出来,只不過是由ANSI與ECMA合作完成,並於1985年正式公布,ISO/IEC小组成立後,這一成果被期收錄,並改名為ISO/IEC 8859 前四個项目。 大家其實發现以上15个字符集中並没有代號為“ISO/IEC 8859 -12”的字符集,據說-12號本来是预留给印度天城梵文的,但後來卻搁置了(因為印度阿三有了自己的编码-ISCII)。由於英语没有任何重音字母,故可使用以上十五个字符集中的任何一个来表示。

 




ISO/IEC 10646 / UCS

1993年,ISO/IEC 10646標準第一次發表,ISO/IEC 10646是ISO 646的擴展,定義了1個31位的字符集。ISO 10646 標準中定義的字符集為UCS,UCS是Universal Character Set的缩写,中文譯作通用字符集。

版本:

  • ISO/IEC 10646-1:第一次發表於1993年,现在的公開版本是2000年發表的 ISO/IEC 10646-1:2000。
  • ISO/IEC 10646-2:在2001年發表。

包含字符:

最初的ISO 10646-1:1993的編碼標準,即Unicode 1.1,收錄中国大陸、台湾、日本及韓國通用字符集的漢字共計20,902個,當然每個版本的Unicode標準的字符集所包含的字符数不盡相同,UCS包含了已知語言的所有字符,除了拉丁语、希腊语、斯拉夫语、希伯来语、阿拉伯语、亚美尼亚语、格鲁吉亚语,還包括中文、日文、韩文這樣的方塊文字,此外還包括了大量的圖形、印刷、数学、科学符号。 UCS给每個字符分配一個唯一的代码,並且赋予了一個正式的名字,通常在表示一個Unicode值的十六進制数的前面加上“U+”,例如“U+0041”代表字符“A”。

編碼方案:

UCS 僅僅是一個超大的字符集,關於UCS制定的編碼方案有兩種:UCS-2 和 UCS-4,Unicode默認以UCS-2 編碼。 顧名思義,UCS-2 就是用兩個字節編碼,UCS-4 就是用4個字節(實際上只用了31位,最高位必须為 0)編碼。那麼 UCS-2 其其可以容納的字符数為 65536(2的16次方),而 UCS-4 可以容纳的字符数为2147483648(2的31次方)。其實對於 UCS-2 已经是完全夠用了,基本上可以包含世界所有國家的常用文字,如果需要考虑一些奇怪的字,那麼 UCS-4 則絕對可以滿足了,21億個字符哪怕是整個宇宙也夠用了吧!



UTF

Unicode 誕生,随之而来的計算機網路也發展了起来,Unicode 如何在網路上傳輸也是一個必須考虑的問題,於是在1992年,面向網路傳輸的 UTF 標準出现了。 UTF是Unicode Transformation Format的缩写,中文譯作 Unicode 轉換格式其實我門從現在可以把 Unicode 看成一個標準或組織,而 UCS 就是一個字符集,那麼UCS 在網路中的傳輸標準就是 UTF 了。 前面提到了 UCS 的編碼實現方式為 UCS-2/UTF-16 和 UCS-4/UTF-32,要嘛是每個字符為2個字節,要麻是4個字節。如果一個僅包含基本7位 ASCII 字符的Unicode文件,每個字符都使用2字節的原 Unicode 編碼來傳輸,其第一字節的8位始終為0,這就造成了比较大的浪费。但是,聪明的人門發明了 UTF-8,UTF-8 採用可變字節編碼,這樣可以大大節省頻宽,並增加網路傳輸效率。


UTF-8

互聯網的普及,強烈要求出現一種統一的編碼方式,UTF-8就是在互聯網上使用最廣的一種Unucide的實現方式,其他實現方式UTF-16 (字符用兩個字節或四個字節表示) 和 UTF-32 (字符用四個字節表示)。

使用1~4個字節為每個 UCS 中的字符編碼:
  • 128個ASCII字符只需一個字節编码(Unicode範圍由U+0000至U+007F)
  • 拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母需要二個字節编码(Unicode范围由U+0080至U+07FF)
  • 大部分國家的常用字(包括中文)使用三個字節編碼
  • 其他極少使用的罕見字符使用四字節編碼
  • 在處理經常會用到的ASCII字符方面非常有效,在處理擴展的拉丁符集方面也不比UTF-16差,對於中文字字符來說,比UTF-32要好。
  • 他是一種可變長的編碼方式,他可以使用1~4個字節表示一個符號,根據不同的符號而變化字節長度
  • 字節第一位設為0,後面7位為這個符號Unicode碼,因此對於英文字母,UTF-8編碼和 ASCII 碼是相同的


優點


  • UTF-8是ASCII的一個超集。因為一個純ASCCII字符串也是一個合法的UTF-8字符串。所以現存的ASCII 本文不需要轉換。為傳統的拓展 ASCII 字符集設計的軟件通常可以不經修改或很少修改就能與UTF-8 一起使用。
  • 使用標準的面向字節的排序過程對 UTF-8 排序產生與 Unucide 代碼點排序相同的結果。
  • UTF-8 和 UTF-16都是可擴展標記與顏文檔的標準編碼。所有其他編碼都必須通過顯示或本文聲明來指定。
  • 任何面向字節的字符串搜索算法都可以用於UTF-8的數據


缺點


  • 因為每個字符使用不同數量的字符編碼,所以尋找串中的第N個字符事一個O(N)的複雜的操作,串越長,則需要更多的時間來定位特定的字符,同時,還需要變換來字符編碼成字節,把字節解碼成字符。

UTF-16/UCS-2

UCS-2的父集,使用2個或4個字節来為每個UCS中的字符编码



UTF-32/UCS-4

等同於 UCS-4,對於所有字符都使用四個字節来編碼,對空間而言是非常沒有效率。UTF-32又稱UCS-4。


BOM是什麼?

BOM是byte-order mark的縮寫,為Unicode標準用來區分一個文件是UTF-8還是UTF-16或UTF-32编码方式的記號,又稱字節序(位元組順序記號)

UTF-8以單字節為編碼單元,並沒有字節序的問題,而UTF-16以兩個字節為編碼单元,在解释一個UTF-16,首先要弄清楚每個編碼的字節序。例如“奎”的Unicode編碼是594E,“乙”的Unicode编碼是4E59。如果我門收到UTF-16字節流“594E”,那這是“奎”還是“乙? 那UTF-16文件開頭的BOM就有作用了。
採用Unicode編碼方式的文件如果開頭出现了"FEFF","FEFF" 在UCS中是不存在的字符,也叫做“ZERO WIDTH NO-BREAK SPACE”,那麼就表明這個文件的字節流是Big-Endian(高字節在前,高位元組)的;如果收到“FFFE”,就表明字節流是Little-Endian(低字節在前,低位元組)。







  • Unicode : 編碼是4個字節  "FF FE 25 4E",其中"FF FE" ,表示低字節在前,真正編碼是4E25(對調讀才是正確)
  • Unicode big endian : 編碼是4個字節  "FE FF 4E 25",其中"FF FE" ,表示高字節在前。
  • UTF-8 : 編碼是六個字節 "EF BB BF E9 BC A0",前三個字節 "EF BB BF" 表示這是UTF-8編碼,真正的編碼則是"E9 BC A0" 等於"鼠",他的順序與編碼順序是一致的。有"EF BB BF" 代表有BOM,沒有的話代表無 BOM
  • 在UTF-8文件中放置BOM主要是微软的習慣,BOM其實是為UTF-16和UTF-32準備的,微软在UTF-8使用BOM是因為這樣可以把UTF-8和ASCII等編碼明確區分開。

    UTF-8不需要BOM來表明字元順序,但可以用BOM來表明編碼方式。字串"ZERO WIDTH NO-BREAK SPACE"的UTF-8編碼是EF BB BF。所以如果接收者收到以EF BB BF開頭的字元流,就知道這是UTF-8編碼了。


    現在以各個編碼系統來看「A」、「a」、「1」及「一」: 
    字 元  Unicode  ASCII  UTF-8 
     A   U+0041   41     41 
     a   U+0061   61     61 
     1   U+0031   31     31 
     一   U+4E00          E4B880





    補充.......










    此時的中國大陸






    GB13000

    前面提到了Unicode的迅速發展,到1993年時包含CJK的Unicode 1.1已经發布了,大陸也意識到了需要一個更大的字符集來走向世界,於是在同一年,中國大陸制定了幾乎等同於Unicode1.1的GB13000.1-93國家編碼標準(簡稱GB13000)。大陸信息產業部把Unicode裡的所有通通拿过来,然後自己重新修訂發布了,改為了國家標準GB13000。此標準等同於 ISO/IEC 10646.1:1993 和 Unicode 1.1。


    GBK

    1995年,在GB13000诞生後不久,中國教育科研網(NCFC)與美國NCFnet直接聯網,這一天是中國被國際承為開始有網際網路的時間。此後網路正式開始在中國大陸接通,個人計算機開始在中國流行,雖然當時只是高富帥才消费得起的產品。
    中國是一個十幾億人口的大國,微軟意識到了中國是一個巨大的市場,當時的微軟也将自己的操作系统市場布局進中国,進入中國随之而来要解决的就是系统的編碼兼容問題。 之前的國家編碼標準GB 2312,基本满足了漢字的計算機處理需要,它所收錄的漢字已經覆盖中国大陸99.75%的使用频率。但對於人名、古漢语等方面出现的罕用字和繁体字,GB 2312不能處理,因此微软利用了GB2312 中未使用的编码空间,收錄了 GB13000 中的所有字符制定了漢字內碼擴展規範GBK(K為漢语拼音 Kuo Zhan中“扩”字的首字母)。所以這一關係其實是大陸把Unicode1.1借鉴過来改名為了GB13000,而微软则利用GB2312中未使用的編碼空间收陸GB13000 制定了GBK。所以GBK是向下完全兼容GB2312的。

    包含字符:

    共收錄21886个字符, 其中漢字21003個, 字符883個

    编码方式:

    GBK只不過是把GB2312中未使用的空間,编码了其他字符,所以GBK同樣是用兩個字節為每個字符進行编碼。

    GB18030

    微軟到了1999年前後,說GBK已经落伍了,现在流行UTF-8標準,準備全盤轉換成UTF-8,但中國不是吃素的,編寫並強制推出了GB18030標準GB18030的誕生還有一個原因是GBK只包含了大部分的漢字和繁體字等,我们的少數民族兄弟根本沒有考虑!中國有56個民族,其中有12個民族有自己的文字,那怎麼辦呢? 在2000年,電子工業標準化研究所起凝了 GB18030 標準,项目代号“GB 18030-2000”,全稱《信息技術-信息交换用漢字編碼字符集-基本集的擴充》。此標準推出后,在中國大陸之後的所售產品必须强制支持GB18030標準,不然不得賣!

    版本:

    • GB 18030-2000
    • GB 18030-2005

    包含字符:

    GB18030收录了GBK中的所有字符,並将Unicode中其他中文字符(少数民族文字、偏僻字)也一並收录进来重新编码。其中GB 18030-2000共收錄27533个漢字,而GB 18030-2005共包含70244个汉字。

    编码方式:

    採用多字節编码,每个個符由1或2或4個字節進行編碼






    關於字元編碼最重要的一件事





    如果不知道某個字串的編碼方式是UTF-8還是ASCII還是 ISO 8859-1(Latin 1)還是 Windows 1252(西歐)根本不可能正確顯示出來,甚至連在哪節數都找不到,大於127的code point有上百種編碼方式。
    我們要如何保存某個字串的編碼資訊呢? 以電子郵件來說,郵件表頭應該會有一個字串 :
    Content-Type: text/plain; charset="UTF-8"


    如果是Web網頁的話利用某些特別的Tag把HTML檔案Content-Type放在HTML檔案裡比較方便,幾乎所有的編碼方式由32到127的字元都是一樣的,所以不需用到怪字母就能在HTML網頁讀到這些資訊 : 
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    不過這個meta tag一定要得放在 <head>段落非常前面的地方。因為網頁瀏覽器一看到這個tag就會停止分析,然後改用你指定的編碼方式重新解是整個網頁。

    如果瀏覽器http header 或 meta tag都找不到 Content-Tpye時會怎麼做? Internet Explorer會做一件很有趣的事 : 他會依據各位元組在各種常見語言編碼中出現的頻率,猜測網頁所用的語言及編碼方式。
    由於各種舊的8位元頁碼通常把該國的字母放在128到255範圍內不同的位置,而各種人類語言的字母使用頻率都有不同的分佈特性,所以這種做法有很大的機會會成功。但等到有一天,當他門寫的內容不符合所用語言的字母頻率分佈時, Internet Explorer就會顯是錯誤。



    相關連結
    http://tgideas.qq.com/webplat/info/news_version3/804/808/811/m579/201307/218730.shtml