需要再一次強調的是,無論歷史上的UCS還是現如今的Unicode,兩者指的都是編碼字符集,而不是字符集編碼。花費一點時間來理解好這件事,然後你會發現對所有網頁的,系統的,編碼標准之間的來回轉換等等繁雜事務都會思路清晰,手到擒來。
首先說說最一般意義上的字符集。
一個抽象字符集其實就是指字符的集合,例如所有的英文字母是一個抽象字符集,所有的漢字是一個抽象字符集,當然,把全世界所有語言的符號都放在一起,也可以稱為一個抽象字符集,所以這個劃分是相當人為的。之所以說“抽象”二字,是因為這裡所提及的字符不是任何具體形式的字符,拿漢字中的“漢”這個字符來說,您在這篇文章中看到的這個“漢”其實是這個字符的一種具體表現形式,是它的圖像表現形式,而且它是用中文(而非拼音)書寫而成,使用宋體外觀;而當人們用嘴發出“漢”這個音的時候,他們是在使用“漢”的另一種具體表現形式——聲音,但無論如何,兩者所指的字符都是“漢”這個字。同一個字符的表現形式可能有無數種(點陣表示,矢量表示,音頻表示,楷體,草書等等等等),把每一種表現形式下的同一個字符都納入到字符集中,會使得集合過於龐大,冗余高,也不好管理。因此抽象字符集中的字符,都是指唯一存在的抽象字符,而忽略它的具體表現形式。
抽象字符集中的諸多字符,沒有順序之分,誰也不能說哪個字符在哪個字符前面,而且這種抽象字符只有人能理解。在給一個抽象字符集合中的每個字符都分配一個整數編號之後(注意這個整數並沒有要求大小),這個字符集就有了順序,就成為了編碼字符集。同時,通過這個編號,可以唯一確定到底指的是哪一個字符。當然,對於同一個字符,不同的字符集所制定的整數編號也不盡相同,例如“兒”這個字,在Unicode中,它的編號是0x513F,(為方便起見,以十六進制表示,但這個整數編號並不要求必須是以十六進制表示)意思是說它是Unicode這個編碼字符集中的第0x513F個字符。而在另一種編碼字符集比如Big5中,這個字就是第0xA449個字符了。這種情況的另一面是,許多字符在不同的編碼字符集中被分配了相同的整數編號,例如英文字母“A”,在ASCII及Unicode中,均是第0x41個字符。我們常說的Unicode字符集,指的就是這種被分配了整數編號的字符集合,但要澄清的是,編碼字符集中字符被分配的整數編號,不一定就是該字符在計算機中存儲時所使用的值,計算機中存儲的字符到底使用什麼二進制整數值來表示,是由下面將要說到的字符集編碼決定的。
字符集編碼決定了如何將一個字符的整數編號對應到一個二進制的整數值,有的編碼方案簡單的將該整數值直接作為其在計算機中的表示而存儲,例如英文字符就是這樣,幾乎所有的字符集編碼方案中,英文字母的整數編號與其在計算機內部存儲的二進制形式都一致。但有的編碼方案,例如適用於Unicode字符集的UTF-8編碼形式,就將很大一部分字符的整數編號作了變換後存儲在計算機中。以“漢”字為例,“漢”的Unicode值為0x6C49,但其編碼為UTF-8格式後的值為0xE6B189(注意到變成了三個字節)。這裡只是舉個例子,關於UTF-8的詳細編碼規則可以參看《Mapping codepoints to Unicode encoding forms》一文,URL為http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-AppendixA#sec3。
我們經常聽說的另一種編碼方案UTF-16,則對Unicode中的前65536個字符編號都不做變換,直接作為計算機存儲時使用的值(對65536以後的字符,仍然要做變換),例如“漢”字的Unicode編號為0x6C49,那麼經過UTF-16編碼後存儲在計算機上時,它的表示仍為0x6C49!。我猜,正是因為UTF-16的存在,使得很多人認為Unicode是一種編碼(實際上,是一個字符集,再次重申),也因此,很多人說Unicode的時候,他們實際上指的是UTF-16。UTF-16提供了surrogate pair機制,使得Unicode中碼位大於65536的那些字符得以表示。
Surrogate pair機制在目前來說實在不常用,甚至連一些UTF-16的實現都不支持,所以我不打算在這裡多加討論,其基本的思想就是用兩個16位的編碼表示一個字符(注意,只對碼位超過65536的字符這麼做)。Unicode如此死抱著16這個數字不放,有歷史的原因,也有實用的原因。
當然還有一種最強的編碼,UTF-32,他對所有的Unicode字符均不做變換,直接使用編號存儲!(俗稱的以不變應萬變),只是這種編碼方案太浪費存儲空間(就連1個字節就可以搞定的英文字符,它都必須使用4個字節),因而盡管使用起來方便(不需要任何轉換),卻沒有得到普及。
記得當初Unicode與UCS還沒成家之時,UCS也是需要人愛,需要人疼的,沒有自己的字符集編碼怎麼成。UCS-2與UCS-4就扮演了這樣的角色。UCS-4與UTF-32除了名字不同以外,思想完全一樣。而UCS-2與UTF-16在對前65536個字符的處理上也完全相同,唯一的區別只在於UCS-2 不支持surrogate pair機制,即是說,UCS-2只能對前65536個字符編碼,對其後的字符毫無辦法。不過現在再談起字符編碼的時候,UCS-2與UCS-4早已成為計算機史學家才會用到的詞匯,就讓它們繼續留在故紙堆裡吧。
下一節我們來說說與中文相關的GB2312和GBK。