1. 概述
本文主要包括以下幾個方面:編碼基本知識,java,系統軟體,url,工具軟體等。
在下面的描述中,将以"中文"兩個字為例,經查表可以知道其GB2312編碼是"d6d0 cec4",Unicode編碼為"4e2d 6587",
UTF編碼就是"e4b8ad e69687"。注意,這兩個字沒有iso8859-1編碼,但可以用iso8859-1編碼來"表示"。
2. 編碼基本知識
最早的編碼是iso8859-1,和ascii編碼相似。但為了友善表示各種各樣的語言,逐漸出現了很多标準編碼,重要的有如下幾個。
2.1. iso8859-1
屬于單位元組編碼,最多能表示的字元範圍是0-255,應用于英文系列。比如,字母a的編碼為0x61=97。
很明顯,iso8859-1編碼表示的字元範圍很窄,無法表示中文字元。但是,由于是單位元組編碼,和計算機最基礎的表示機關一緻,
是以很多時候,仍舊使用iso8859-1編碼來表示。而且在很多協定上,預設使用該編碼。
比如,雖然"中文"兩個字不存在iso8859-1編碼,以gb2312編碼為例,應該是"d6d0 cec4"兩個字元,
使用iso8859-1編碼的時候則将它拆開為4個位元組來表示:"d6 d0 ce c4"(事實上,在進行存儲的時候,也是以位元組為機關處理的)。
而如果是UTF編碼,則是6個位元組"e4 b8 ad e6 96 87"。很明顯,這種表示方法還需要以另一種編碼為基礎。
2.2. GB2312/GBK
這就是漢子的國标碼,專門用來表示漢字,是雙位元組編碼,而英文字母和iso8859-1一緻(相容iso8859-1編碼)。
其中gbk編碼能夠用來同時表示繁體字和簡體字,而gb2312隻能表示簡體字,gbk是相容gb2312編碼的。
2.3. unicode
這是最統一的編碼,可以用來表示所有語言的字元,而且是定長雙位元組(也有四位元組的)編碼,包括英文字母在内。
是以可以說它是不相容iso8859-1編碼的,也不相容任何編碼。不過,相對于iso8859-1編碼來說,uniocode編碼隻是在前面增加了一個0位元組,
比如字母a為"00 61"。
需要說明的是,定長編碼便于計算機處理(注意GB2312/GBK不是定長編碼),而unicode又可以用來表示所有字元,
是以在很多軟體内部是使用unicode編碼來處理的,比如java。
2.4. UTF
考慮到unicode編碼不相容iso8859-1編碼,而且容易占用更多的空間:因為對于英文字母,unicode也需要兩個位元組來表示。
是以unicode不便于傳輸和存儲。是以而産生了utf編碼,utf編碼相容iso8859-1編碼,同時也可以用來表示所有語言的字元,
不過,utf編碼是不定長編碼,每一個字元的長度從1-6個位元組不等。另外,utf編碼自帶簡單的校驗功能。一般來講,英文字母都是用一個位元組表示,
而漢字使用三個位元組。
注意,雖然說utf是為了使用更少的空間而使用的,但那隻是相對于unicode編碼來說,如果已經知道是漢字,
則使用GB2312/GBK無疑是最節省的。不過另一方面,值得說明的是,雖然utf編碼對漢字使用3個位元組,但即使對于漢字網頁,
utf編碼也會比unicode編碼節省,因為網頁中包含了很多的英文字元。
3. java對字元的處理
在java應用軟體中,會有多處涉及到字元集編碼,有些地方需要進行正确的設定,有些地方需要進行一定程度的處理。
3.1. getBytes(charset)
這是java字元串處理的一個标準函數,其作用是将字元串所表示的字元按照charset編碼,并以位元組方式表示。
注意字元串在java記憶體中總是按unicode編碼存儲的。比如"中文",正常情況下(即沒有錯誤的時候)存儲為"4e2d 6587",
如果charset為"gbk",則被編碼為"d6d0 cec4",然後傳回位元組"d6 d0 ce c4"。
如果charset為"utf8"則最後是"e4 b8 ad e6 96 87"。
如果是"iso8859-1",則由于無法編碼,最後傳回 "3f 3f"(兩個問号)。
3.2. new String(charset)
這是java字元串處理的另一個标準函數,和上一個函數的作用相反,将位元組數組按照charset編碼進行組合識别,最後轉換為unicode存儲。
參考上述getBytes的例子,"gbk" 和"utf8"都可以得出正确的結果"4e2d 6587",但iso8859-1最後變成了"003f 003f"(兩個問号)。
因為utf8可以用來表示/編碼所有字元,是以new String( str.getBytes( "utf8" ), "utf8" ) === str,即完全可逆。
3.3. setCharacterEncoding()
該函數用來設定http請求或者相應的編碼。
對于request,是指送出内容的編碼,指定後可以通過getParameter()則直接獲得正确的字元串,如果不指定,
則預設使用iso8859-1編碼,需要進一步處理。參見下述"表單輸入"。值得注意的是在執行setCharacterEncoding()之前,
不能執行任何getParameter()。
java doc上說明:This method must be called prior to reading request parameters or reading input using getReader()。
而且,該指定隻對POST方法有效,對GET方法無效。
分析原因,應該是在執行第一個getParameter()的時候,java将會按照編碼分析所有的送出内容,而後續的getParameter()不再進行分析,
是以setCharacterEncoding()無效。而對于GET方法送出表單是,送出的内容在URL中,一開始就已經按照編碼分析所有的送出内容,
setCharacterEncoding()自然就無效。
對于response,則是指定輸出内容的編碼,同時,該設定會傳遞給浏覽器,告訴浏覽器輸出内容所采用的編碼。
3.4. 處理過程
下面分析兩個有代表性的例子,說明java對編碼有關問題的處理方法。
3.4.1. 表單輸入
User input *(gbk:d6d0 cec4) browser *(gbk:d6d0 cec4) web server iso8859-1(00d6 00d 000ce 00c4) class,
需要在class中進行處理:getbytes("iso8859-1")為d6 d0 ce c4,new String("gbk")為d6d0 cec4,記憶體中以unicode編碼則為4e2d 6587。
l 使用者輸入的編碼方式和頁面指定的編碼有關,也和使用者的作業系統有關,是以是不确定的,上例以gbk為例。
l 從browser到web server,可以在表單中指定送出内容時使用的字元集,否則會使用頁面指定的編碼。
而如果在url中直接用?的方式輸入參數,則其編碼往往是作業系統本身的編碼,因為這時和頁面無關。上述仍舊以gbk編碼為例。
l Web server接收到的是位元組流,預設時(getParameter)會以iso8859-1編碼處理之,結果是不正确的,是以需要進行處理。
但如果預先設定了編碼(通過request. setCharacterEncoding ()),則能夠直接擷取到正确的結果。
l 在頁面中指定編碼是個好習慣,否則可能失去控制,無法指定正确的編碼。
3.4.2. 檔案編譯
假設檔案是gbk編碼儲存的,而編譯有兩種編碼選擇:gbk或者iso8859-1,前者是中文windows的預設編碼,後者是linux的預設編碼,
當然也可以在編譯時指定編碼。
Jsp *(gbk:d6d0 cec4) java file *(gbk:d6d0 cec4) compiler read uincode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4)
compiler write utf(gbk: e4b8ad e69687; iso8859-1: *) compiled file unicode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4) class。
是以用gbk編碼儲存,而用iso8859-1編譯的結果是不正确的。
class unicode(4e2d 6587) system.out / jsp.out gbk(d6d0 cec4) os console / browser。
l 檔案可以以多種編碼方式儲存,中文windows下,預設為ansi/gbk。
l 編譯器讀取檔案時,需要得到檔案的編碼,如果未指定,則使用系統預設編碼。一般class檔案,是以系統預設編碼儲存的,
是以編譯不會出問題,但對于jsp檔案,如果在中文windows下編輯儲存,而部署在英文linux下運作/編譯,則會出現問題。
是以需要在jsp檔案中用pageEncoding指定編碼。
l Java編譯的時候會轉換成統一的unicode編碼處理,最後儲存的時候再轉換為utf編碼。
l 當系統輸出字元的時候,會按指定編碼輸出,對于中文windows下,System.out将使用gbk編碼,
而對于response(浏覽器),則使用jsp檔案頭指定的contentType,或者可以直接為response指定編碼。
同時,會告訴browser網頁的編碼。如果未指定,則會使用iso8859-1編碼。對于中文,應該為browser指定輸出字元串的編碼。
l browser顯示網頁的時候,首先使用response中指定的編碼(jsp檔案頭指定的contentType最終也反映在response上),
如果未指定,則會使用網頁中meta項指定中的contentType。
3.5. 幾處設定
對于web應用程式,和編碼有關的設定或者函數如下。
3.5.1. jsp編譯
指定檔案的存儲編碼,很明顯,該設定應該置于檔案的開頭。例如:。另外,對于一般class檔案,可以在編譯的時候指定編碼。
3.5.2. jsp輸出
指定檔案輸出到browser是使用的編碼,該設定也應該置于檔案的開頭。該設定和response.setCharacterEncoding("GBK")等效。
3.5.3. meta設定
指定網頁使用的編碼,該設定對靜态網頁尤其有作用。因為靜态網頁無法采用jsp的設定,而且也無法執行response.setCharacterEncoding()。例如:
如果同時采用了jsp輸出和meta設定兩種編碼指定方式,則jsp指定的優先。因為jsp指定的直接展現在response中。
需要注意的是,apache有一個設定可以給無編碼指定的網頁指定編碼,該指定等同于jsp的編碼指定方式,是以會覆寫靜态網頁中的meta指定。
是以有人建議關閉該設定。
3.5.4. form設定
當浏覽器送出表單的時候,可以指定相應的編碼。例如:
。一般不必不使用該設定,浏覽器會直接使用網頁的編碼。