?

計算機中的字是如何處理的?

?

??? 如果你用放大鏡看一下,可以看出屏幕上的字是由一個一個的像素點組成的,每一個字符用一組像素點拼接出來,這些像素點組成一幅圖像,變成了我們的文字,計算機又是如何將我們的文字保存起來的呢?是用一個個的點組成的圖像將文字保存起來的嗎?當然不是,讓我們從英文開始,由于英文是拼音文字,實際上所有的英文字符和符號加起來也不超過100個,在我們的文字中存在著如此大量的重復符號,這就意味著保存每個字符的圖像會有大量的重復,比如 e 就是出現最多的符號等等。所以在計算機中,實際上不會保存字符的圖像。

?

什么是字符編碼?

?

??? 由于我們的文字中存在著大量的重復字符,而計算機天生就是用來處理數字的,為了減少我們需要保存的信息量,我們可以使用一個數字編碼來表示每一個字符,通過對每一個字符規定一個唯一的數字代號,然后,對應每一個代號,建立其相對應的圖形,這樣,在每一個文件中,我們只需要保存每一個字符的編碼就相當于保存了文字,在需要顯示出來的時候,先取得保存起來的編碼,然后通過編碼表,我們可以查到字符對應的圖形,然后將這個圖形顯示出來,這樣我們就可以看到文字了,這些用來規定每一個字符所使用的代碼的表格,就稱為編碼表。編碼就是對我們日常使用字符的一種數字編號。

?

第一個編碼表 ASCII

?

在最初的時候,美國人制定了第一張編碼表 《美國標準信息交換碼》,簡稱 ASCII,它總共規定了 128 個符號所對應的數字代號,使用了 7 位二進制的位來表示這些數字。其中包含了英文的大小寫字母、數字、標點符號等常用的字符,數字代號從 0 127ASCII 的表示內容如下:

0 – 31 ??????? 控制符號

32????????????? 空格

33-47?????????? 常用符號

48-57?????????? 數字

58-64?????????? 符號

65-90?????????? 大寫字母

91-96?????????? 符號

97-127????????? 小寫字母

?

注意,32 表示空格,雖然我們再紙上寫字時,只要手腕動一下,就可以流出一個空格,但是,在計算機上,空格與普通得字符一樣也需要用一個編碼來表示,33-127 95個編碼用來表示符號,數字和英文的大寫和小寫字母。比如數字 1 所對應的數字代號為 49,大寫字母 A 對應的代號為 65, 小寫字母 a 對應的代號為 97。所以,我們所寫的代碼 hello, world 保存在文件中時,實際上是保存了一組數字 104 101 108 108 111 44 32 119 111 114 108 100。我們再程序中比較英文字符串的大小時,實際上也是比較字符對應的 ASCII 的編碼大小。

??? 由于 ASCII 出現最早,因此各種編碼實際上都受到了它的影響,并盡量與其相兼容。

?

擴展 ASCII 編碼 ISO8859

?

??? 美國人順利解決了字符的問題,可是歐洲的各個國家還沒有,比如法語中就有許多英語中沒有的字符,因此 ASCII 不能幫助歐洲人解決編碼問題。

為了解決這個問題,人們借鑒 ASCII 的設計思想,創造了許多使用 8 位二進制數來表示字符的擴充字符集,這樣我們就可以使用256種數字代號了,表示更多的字符了。在這些字符集中,從 0 - 127 的代碼與 ASCII 保持兼容,從 128 255 用于其它的字符和符號,由于有很多的語言,有著各自不同的字符,于是人們為不同的語言制定了大量不同的編碼表,在這些碼表中,從 128 - 255 表示各自不同的字符,其中,國際標準化組織的 ISO8859 標準得到了廣泛的使用。

ISO8859 的編碼表中,編號 0 – 127 ASCII 保持兼容,編號128 – 159 32個編碼保留給擴充定義的 32 個擴充控制碼,160 為空格, 161 -255 95 個數字用于新增加的字符代碼。編碼的布局與 ASCII 的設計思想如出一轍,由于在一張碼表中只能增加 95 種字符的代碼,所以 ISO8859 實際上不是一張碼表,而是一系列標準,包括 14 個字符碼表。例如,西歐的常用字符就包含在 ISO8859-1字符表中。在 ISO8859-7種則包含了 ASCII 和現代希臘語字符。

?

問題出現了!

?

??? ISO 8859標準解決了大量的字符編碼問題,但也帶來了新的問題,比如說,沒有辦法在一篇文章中同時使用 ISO8859-1 ISO8859-7,也就是說,在同一篇文章中不能同時出現希臘文和法文,因為他們的編碼范圍是重合的。例如:在 ISO8859-1 217號編碼表示字符ù ,而在 ISO8859-7中則表示希臘字符Ω,這樣一篇使用 ISO8859-1 保存的文件,在使用 ISO8859-7編碼的計算機上打開時,將看到錯誤的內容。為了同時處理一種以上的文字,甚至還出現了一些同時包含原來不屬于同一張碼表的字符的新碼表。

?

大字符集的煩惱

?

??? 不管如何,歐洲的拼音文字都還可以用一個字節來保存,一個字節由8個二進制的位組成,用來表示無符號的整數的話,范圍正好是 0 – 255

但是,更嚴重的問題出現在東方,中國,朝鮮和日本的文字包含大量的符號。例如,中國的文字不是拼音文字,漢字的個數有數萬之多,遠遠超過區區 256 個字符,因此 ISO 8859 標準實際上不能處理中文的字符。

通過借鑒 ISO8859 的編碼思想,中國的專家靈巧的解決了中文的編碼問題。

既然一個字節的 256 種字符不能表示中文,那么,我們就使用兩個字節來表示一個中文,在每個字符的 256 種可能中,低于 128 的為了與 ASCII 保持兼容,我們不使用,借鑒 ISO8859的設計方案,只使用從 160 以后的 96 個數字,兩個字節分成高位和低位,高位的取值范圍從 176-247 72個,低位從 161 – 25494這樣,兩個字節就有 72 * 94 = 6768種可能,也就是可以表示 6768 種漢字,這個標準我們稱為 GB2312-80

6768 個漢字顯然不能表示全部的漢字,但是這個標準是在1980年制定的,那時候,計算機的處理能力,存儲能力都還很有限,所以在制定這個標準的時候,實際上只包含了常用的漢字,這些漢字是通過對日常生活中的報紙,電視,電影等使用的漢字進行統計得出的,大概占常用漢字的 99%。因此,我們時常會碰到一些名字中的特殊漢字無法輸入到計算機中的問題,就是由于這些生僻的漢字不在 GB2312 的常用漢字之中的緣故。

由于 GB2312 規定的字符編碼實際上與 ISO8859 是沖突的,所以,當我們在中文環境下看一些西文的文章,使用一些西文的軟件的時候,時常就會發現許多古怪的漢字出現在屏幕上,實際上就是因為西文中使用了與漢字編碼沖突的字符,被我們的系統生硬的翻譯成中文造成的。

不過,GB2312 統一了中文字符編碼的使用,我們現在所使用的各種電子產品實際上都是基于 GB2312 來處理中文的。

GB2312-80 僅收漢字6763個,這大大少于現有漢字,隨著時間推移及漢字文化的不斷延伸推廣,有些原來很少用的字,現在變成了常用字,例如:朱镕基的“镕”字,未收入GB2312-80,現在大陸的報業出刊只得使用(金+容)、(金容)、(左金右容)等來表示,形式不一而同,這使得表示、存儲、輸入、處理都非常不方便,而且這種表示沒有統一標準。

為了解決這些問題,全國信息技術化技術委員會于1995121日《漢字內碼擴展規范》。GBK向下與GB2312完全兼容,向上支持ISO 10646國際標準,在前者向后者過渡過程中起到的承上啟下的作用。GBK 亦采用雙字節表示,總體編碼范圍為8140-FEFE之間,高字節在81-FE之間,低字節在40-FE之間,不包括7F。在 GBK 1.0 中共收錄了 21886個符號,漢字有21003個。

GBK 共收入21886個漢字和圖形符號,包括:

* GB2312 中的全部漢字、非漢字符號。

* BIG5 中的全部漢字。

* ISO 10646相應的國家標準GB13000中的其它CJK漢字,以上合計20902個漢字。

* 其它漢字、部首、符號,共計984個。

?

微軟公司自Windows 95 簡體中文版開始支持GBK代碼,但目前的許多軟件都不能很好地支持GBK漢字。

GBK 編碼區分三部分:

* 漢字區 包括

GBK/2 OXBOA1-F7FE, 收錄GB2312漢字6763個,按原序排列;

GBK/3 OX8140-AOFE,收錄CJK漢字6080個;

GBK/4 OXAA40-FEAO,收錄CJK漢字和增補的漢字8160個。

* 圖形符號區 包括

GBK/1 OXA1A1-A9FE,除GB2312的符號外,還增補了其它符號

GBK/5 OXA840-A9AO,擴除非漢字區。

* 用戶自定義區

GBK區域中的空白區,用戶可以自己定義字符。

?

GB18030 是最新的漢字編碼字符集國家標準, 向下兼容 GBK GB2312 標準。 GB18030 編碼是一二四字節變長編碼。 一字節部分從 0x0~0x7F ASCII 編碼兼容。二字節部分, 首字節從 0x81~0xFE, 尾字節從 0x40~0x7E 以及 0x80~0xFE, GBK標準基本兼容。 四字節部分, 第一字節從 0x81~0xFE, 第二字節從 0x30~0x39, 第三和第四字節的范圍和前兩個字節分別相同。

?

不一樣的中文

?

中文的問題好像也解決了,且慢,新的問題又來了。

中國的臺灣省也在使用中文,但是由于歷史的原因,那里沒有使用大陸的簡體中文,還在使用著繁體的中文,并且他們自己也制定了一套表示繁體中文的字符編碼,稱為 BIG5,不幸的是,雖然他們的也使用兩個字節來表示一個漢字,但他們沒有象我們兼容 ASCII 一樣兼容大陸的簡體中文,他們使用了大致相同的編碼范圍來表示繁體的漢字。天哪! ISO8859 的悲劇又出現在同樣使用漢字的中國人身上了,同樣的編碼在大陸和臺灣的編碼中實際上表示不同的字符,大陸的玩家在玩臺灣的游戲時,經常會遇到亂碼的問題,問題根源就在于,大陸的計算機默認字符的編碼就是 GB2312, 當碰到臺灣使用 BIG5 編碼的文字時,就會作出錯誤的轉換。

?

由于歷史和文化的原因,日文和韓文中也包含許多的漢字,象漢字一樣擁有大量的字符,不幸的是,他們的字符編碼也同樣與中文編碼有著沖突,日文的游戲在大陸上一樣也會出現無法理解的亂碼。《中文之星》,《南極星》,《四通利方》就是用于在這些編碼中進行識別和轉換的專用軟件。

?

互聯的時代

?

在二十世紀八十年代后期,互聯網出現了,一夜之間,地球村上的人們可以直接訪問遠在天邊的服務器,電子文件在全世界傳播,在一切都在數字化的今天,文件中的數字到底代表什么字?這可真是一個問題。

?

UNICODE

?

實際上問題的根源在于我們有太多的編碼表。

?

如果整個地球村都使用一張統一的編碼表,那么每一個編碼就會有一個確定的含義,就不會有亂碼的問題出現了。

實際上,在80年代就有了一個稱為 UNICODE 的組織,這個組織制定了一個能夠覆蓋幾乎任何語言的編碼表,在 Unicode3.0.1中就包含了 49194 個字符,將來,Unicode 中還會增加更多的字符。Unicode 的全稱是 Universal Multiple-Octet Coded Character Set ,簡稱為 UCS

由于要表示的字符如此之多,所以一開始的 Unicode1.0編碼就使用連續的兩個字節也就是一個WORD 來表示編碼,比如“漢”的UCS 編碼就是 6C49。這樣在 Unicode 的編碼中就可以表示 256*256 = 65536 種符號了。

直接使用一個WORD 相當于兩個字節來保存編碼可能是最為自然的 Unicode 編碼的方式,這種方式被稱為 UCS-2,也被稱為 ISO 10646,,在這種編碼中,每一個字符使用兩個字節來進行表示,例如,“中” 使用 11598 來編碼,而大寫字母 A 仍然使用 65 表示,但它占用了兩個字節,高位用 0 來進行補齊。

?

由于每個WORD 表示一個字符,但是在不同的計算機上,實際上對 WORD 有兩種不同的處理方式,高字節在前,或者低字節在前,為了在UCS-2編碼的文檔中,能夠區分到底是高字節在前,還是低字節在前,使用 UCS-2 的文檔使用了一組不可能在UCS-2種出現的組合來進行區分,通常情況下,低字節在前,高字節在后,通過在文檔的開頭增加 FFFE 來進行表示。高字節在前,低字節在后,稱為大頭在前,即Big Endian,使用 FFFE 來進行表示。這樣,程序可以通過文檔的前兩個字節,立即判斷出該文檔是否高字節在前。

Endian 這個詞出自 《格列佛游記》,小人國的內戰就源于吃雞蛋時要先吃大頭 big endian 還是小頭 little-endian,并由此發生了內戰。

?

理想與現實

?

UCS-2 雖然理論上可以統一編碼,但仍然面臨著現實的困難。

首先,UCS-2 不能與現有的所有編碼兼容,現有的文檔和軟件必須針對 Unicode 進行轉換才能使用。即使是英文也面臨著單字節到雙字節的轉換問題。

其次,許多國家和地區已經以法律的形式規定了其所使用的編碼,更換為一種新的編碼不現實。比如在中國大陸,就規定 GB2312 是大陸軟件、硬件編碼的基礎。

第三,現在還有使用中的大量的軟件和硬件是基于單字節的編碼實現的,UCS-2 的雙字節表示的字符不能可靠的在其上工作。

?

新希望 UTF-8

?

為了盡可能與現有的軟件和硬件相適應,美國人又制定了一系列用于傳輸和保存Unicode 的編碼標準 UTF,這些編碼稱為UCS 傳輸格式碼,也就是將 UCS 的編碼通過一定的轉換,來達到使用的目的。常見的有 UTF-7UTF-8UTF-16等。

其中 UTF-8 編碼得到了廣泛的應用,UTF-8 的全名是UCS Transformation Format 8, UCS 編碼的8位傳輸格式,就是使用單字節的方式對 UCS 進行編碼,使 Unicode 編碼能夠在單字節的設備上正常進行處理。

UTF-8 編碼是變長的編碼,對不同的 Unicode 可能編成不同的長度

?

UCS-2?????????????????????????? UTF-8

0000-007F??? 0-? 127??????????? 0xxxxxxx

0080-07FF? 128- 2047??????????? 110xxxxx 10xxxxxx

??? 0800-FFFF 2048-65535??????????? 1110xxxx 10xxxxxx 10xxxxxx

?

? 例如 1 Unicode 編碼是 31 00, 0-127之間,所以轉換后即為 31,而“中”字的UTF-8 Unicode 編碼為 11598,轉換成 UTF-8則為 e4 b8 ad

?

實際上,ASCII 字符用 UTF-8 來表示后,與 ASCII 是完全一樣的,美國人又近水樓臺的把自己的問題解決了。但其他的編碼就沒有這么幸運了。

?

突破障礙 - Unicode 與 本地編碼的轉換

?

UTF-8 編碼解決了字符的編碼問題,又可以在現有的設備上通行,因此,得到了廣泛的使用,

?

在人間

?

??? XML 中的問題

??? XML 的設計目標是實現跨網絡,跨國界的信息表示,所以,在XML 設計之初,就規定 XML 文件的默認編碼格式就是 UTF-8,也就是說,如果沒有特殊的說明,XML文件將被視為 UTF-8 編碼。

然而,大部分的中文編輯軟件,是根據操作系統來決定編碼的方式的,所以,在寫字板中直接輸入并保存的文件,將被保存為 GB2312 編碼,所以,在讀出 XML文件內容時,往往就會出現文件錯誤的提示了。這種情況會出現在文件中有中文出現的時候,如果沒有中文,只有英文信息,就不會出現問題。原因很簡單,有中文時,因為中文的編碼并不是UTF-8 編碼,所以會造成沖突,沒有中文時,英文的編碼在GB2312 中與ASCII是兼容的,而ASCII UTF-8 是完全一致的,所以不會出現問題。這種情況也包括 UltraEdit 軟件。

但時,專業的 XML編輯軟件會自動將內容保存為 UTF-8 編碼,不會有問題。

在通過DOMXSLT保存 XML 文件時也有著同樣的問題。

默認情況下,XML 的處理程序一般會將內容作為 UTF-8 編碼進行處理,所以保存下來的 XML 文件必須要用可以識別 UTF-8 的軟件來進行查看,如Windows 的記事本。

?

??? Java 的處理

Java 的設計目標是一次編寫,到處運行,所以在 Java 的內部對字符的處理采用了 UCS 來處理,因此 Java 的字符類型不再是 C++ 中的一個字節,而使用兩個字節來保存一個字符。

但是,我們會發現,在 Java 的文件流中保存為文件后,我們可以直接使用記事本或 UltraEdit 打開察看。

在這里,Java 采用了一個靈巧的默認轉換機制,當需要將內容中的字符保存到文件中時,Java 會自動的查看一下系統的本地編碼,系統的本地編碼可以在控制面板中查到,然后,自動將 UCS 編碼的字符轉換為本地編碼,并進行保存。當需要從系統的文件系統中讀入一個文件時,Java 通過查看系統的本地編碼來決定如何識別文件的內容。這樣,Java 就可以在內部使用 UCS, 但用戶可以直接使用本地編碼的文件了。

Java 在相應的方法中,提供了額外的參數,可以讓用戶自己來指定文件的編碼。

?

??? .Net 的處理

??? 在微軟的 .Net 內部,同樣使用 UCS 編碼,但是,在對文件進行處理的時候,與Java 有一些區別,.Net 不查詢系統的本地編碼,而是直接使用磨人的 UTF-8 編碼進行文件的處理,所以,你保存的中文內容,在 UltraEdit 中可能就是亂碼,但是,如果你使用記事本打開的話,就不會有問題,因為 Windows 的記事本可以識別 UTF-8 的編碼。

.Net 軟件的配置文件使用 XML 格式,默認的編碼一樣是 UTF-8 ,所以,必須使用可以識別 UTF-8 的軟件進行處理,如:vs.net,記事本等。

.Net 中,網頁默認處理編碼就是 UTF-8

?

??? Web 中的問題

網頁的編碼問題主要有兩點,一是網頁是如何編碼的,二是如何告訴瀏覽器如何編碼的。

第一個問題,又可以分成靜態頁面和動態頁面兩個問題。

對靜態頁面,網頁的編碼要看你保存文件時的編碼選項,多數的網頁編輯軟件可以讓你選擇編碼的類型,默認為本地編碼,為了使網頁減少編碼的問題,最好保存為 UTF-8 編碼格式。

對動態頁面,如 Servlet 生成的頁面,在 HttpServletResponse 類中有一個方法 setContentType,可以通過參數來指定生成的頁面的類型和編碼,例如:response.setContentType("text/html; charset=utf-8");來指定生成的頁面的編碼類型。

jsp 頁面可以通過 <%@ page contentType="text/html;charset=gb2312" %> 來指定生成的頁面的編碼及類型。

第二個問題,如何通知瀏覽器網頁的編碼類型。

瀏覽器收到只是一個字節流,它并不知道頁面是如何編碼的,因此,需要一個機制來告訴瀏覽器頁面的編碼類型,標準的機制是使用 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 來指定頁面的編碼,當瀏覽器讀取頁面遇到這樣的指示時,將使用這里制定的編碼方式重新加載頁面。

否則的話,瀏覽器將會試圖猜出頁面的編碼類型。

?

??? Tomcat 中的中文問題

?

Tomcat 中,經常遇到取回客戶端提交的信息是亂碼的問題。

當提交表單的時候,HTML頁面的Form標簽會使情況變得更為復雜。瀏覽器的編碼方式取決于當前頁面的編碼設定,對Form標簽也照此處理。這意味著如果ASCII格式的HTML頁面用ISO-8859-1編碼,那么用戶在此頁面中將不能提交中文字符。所以,如果你的頁面使用的是 utf-8,那么 POST 的時候,也將使用 utf-8

由于 Tomcat 是美國人設計的,Tomcat 默認使用ISO8859-1 編碼隊客戶端返回的內容進行解碼,由于編碼與內容不一致,就會出現亂碼的 ??? 出現,根據以上的分析,在服務器端讀取客戶端回送的內容時,需要首先設定回送內容的編碼,然后再進行信息的讀取,通過使用 HttpServletRequest 的方法 setCharacterEncoding("utf-8")先行設定信息的編碼類型。然后,就可以正確讀取內容了。

?

總結

?

編碼問題是信息處理的基本問題,但是由于歷史和政治的問題,事實上存在著大量不統一的編碼方式,造成在信息處理過程中的信息丟失,轉換錯誤等問題,UCS 為問題的解決提供了一個很好的方向,但是,在現在的軟件環境中,還沒有達到全面地使用。在實際中工作中應盡量采用統一的編碼格式,減少編碼問題的發生

?

?

?

?