計算機中的字是如何處理的?
???
如果你用放大鏡看一下,可以看出屏幕上的字是由一個一個的像素點組成的,每一個字符用一組像素點拼接出來,這些像素點組成一幅圖像,變成了我們的文字,計算機又是如何將我們的文字保存起來的呢?是用一個個的點組成的圖像將文字保存起來的嗎?當然不是,讓我們從英文開始,由于英文是拼音文字,實際上所有的英文字符和符號加起來也不超過
100
個,在我們的文字中存在著如此大量的重復符號,這就意味著保存每個字符的圖像會有大量的重復,比如
e
就是出現最多的符號等等。所以在計算機中,實際上不會保存字符的圖像。
什么是字符編碼?
???
由于我們的文字中存在著大量的重復字符,而計算機天生就是用來處理數字的
,
為了減少我們需要保存的信息量,我們可以使用一個數字編碼來表示每一個字符,通過對每一個字符規定一個唯一的數字代號,然后,對應每一個代號,建立其相對應的圖形,這樣,在每一個文件中,我們只需要保存每一個字符的編碼就相當于保存了文字,在需要顯示出來的時候,先取得保存起來的編碼,然后通過編碼表,我們可以查到字符對應的圖形,然后將這個圖形顯示出來,這樣我們就可以看到文字了,這些用來規定每一個字符所使用的代碼的表格,就稱為編碼表。編碼就是對我們日常使用字符的一種數字編號。
第一個編碼表
ASCII
在最初的時候,美國人制定了第一張編碼表《美國標準信息交換碼》,簡稱
ASCII
,它總共規定了
128
個符號所對應的數字代號,使用了
7
位二進制的位來表示這些數字。其中包含了英文的大小寫字母、數字、標點符號等常用的字符,數字代號從
0
至
127
,
ASCII
的表示內容如下:
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 – 254
共
94
這樣,兩個字節就有
72 * 94 = 6768
種可能,也就是可以表示
6768
種漢字,這個標準我們稱為
GB2312-80
。
6768
個漢字顯然不能表示全部的漢字,但是這個標準是在
1980
年制定的,那時候,計算機的處理能力,存儲能力都還很有限,所以在制定這個標準的時候,實際上只包含了常用的漢字,這些漢字是通過對日常生活中的報紙,電視,電影等使用的漢字進行統計得出的,大概占常用漢字的
99%
。因此,我們時常會碰到一些名字中的特殊漢字無法輸入到計算機中的問題,就是由于這些生僻的漢字不在
GB2312
的常用漢字之中的緣故。
由于
GB2312
規定的字符編碼實際上與
ISO8859
是沖突的,所以,當我們在中文環境下看一些西文的文章,使用一些西文的軟件的時候,時常就會發現許多古怪的漢字出現在屏幕上,實際上就是因為西文中使用了與漢字編碼沖突的字符,被我們的系統生硬的翻譯成中文造成的。
不過,
GB2312
統一了中文字符編碼的使用,我們現在所使用的各種電子產品實際上都是基于
GB2312
來處理中文的。
GB2312-80
僅收漢字
6763
個,這大大少于現有漢字,隨著時間推移及漢字文化的不斷延伸推廣,有些原來很少用的字,現在變成了常用字,例如:朱镕基的
“
镕
”
字,未收入
GB2312-80
,現在大陸的報業出刊只得使用(金
+
容)、(金容)、(左金右容)等來表示,形式不一而同,這使得表示、存儲、輸入、處理都非常不方便,而且這種表示沒有統一標準。
為了解決這些問題,全國信息技術化技術委員會于
1995
年
12
月
1
日
《漢字內碼擴展規范》。
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-7
,
UTF-8
,
UTF-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
編碼,不會有問題。
在通過
DOM
或
XSLT
保存
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
為問題的解決提供了一個很好的方向,但是,在現在的軟件環境中,還沒有達到全面地使用。在實際中工作中應盡量采用統一的編碼格式,減少編碼問題的發生
?