java編碼之間轉換

不設限發表於2011-12-20

因為url傳送預設編碼是容器的編碼,tomcat預設是iso-8859-1.所以,request.gerParameter()獲得的值必須轉碼,除非設定tocmat的預設url編碼。  

這個是涉及到字元編碼的問題   一個字元在網頁間傳遞要經過編/解碼的問題   
  我來具體解釋一下這個語句username=new   String(username.getBytes("ISO8859_1"),"GBK");   
  網頁本身是gb2312(也就是gbk)對資料進行解碼的,那麼你要將這個資料轉換成ISO8859_1解碼   
  的資料,一定要先將這個資料編碼成gbk,然後通過getBytes()方法將其解碼成ISO8859_1編碼方式,那麼最後得到的資料才是以ISO8859_1進行編碼的資料   

1、函式介紹
在Java中,字串用統一的Unicode編碼,每個字元佔用兩個位元組,與編碼有關的兩個主要函式為:
1)將字串用指定的編碼集合解析成位元組陣列,完成Unicode-〉charsetName轉換
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException 
2)將位元組陣列以指定的編碼集合構造成字串,完成charsetName-〉Unicode轉換
public String(byte[] bytes, String charsetName) throws UnsupportedEncodingException
2、Unicode與各編碼之間的直接轉換
下面以對中文字串"a中文"的編碼轉換為例,來了解各種編碼之間的轉換
1)Unicode和GBK
測試結果如下,每個漢字轉換為兩個位元組,且是可逆的,即通過位元組可以轉換回字串
StringGBKByteArray:/u0061/u4E2D/u6587(a中文)-〉0x61 0xD6 0xD0 0xCE 0xC4
ByteArrayGBKString:0x61 0xD6 0xD0 0xCE 0xC4-〉/u0061/u4E2D/u6587(a中文)
2)Unicode和UTF-8
測試結果如下,每個漢字轉換為三個位元組,且是可逆的,即通過位元組可以轉換回字串
StringUTF-8ByteArray:/u0061/u4E2D/u6587(a中文)-〉0x61 0xE4 0xB8 0xAD 0xE6%0x96 0x87
ByteArrayUTF-8String:0x61 0xE4 0xB8 0xAD 0xE6%0x96 0x87-〉/u0061/u4E2D/u6587(a中文)
3)Unicode和ISO-8859-1
測試結果如下,當存在漢字時轉換失敗,非可逆,即通過位元組不能再轉換回字串
StringISO-8859-1ByteArray:/u0061/u4E2D/u6587(a中文)-〉0x61 0x3F 0x3F
ByteArrayISO-8859-1String:0x61 0x3F 0x3F-〉/u0061/u003F/u003F(a??)
3、Unicode與各編碼之間的交叉轉換
在上面直接轉換中,由字串(Unicode)生成的位元組陣列,在構造回字串時,使用的是正確的編碼集合,如果使用的不是正確的編碼集合會怎樣呢?會正確構造嗎?如果不能正確構造能有辦法恢復嗎?會資訊丟失嗎?
下面我們就來看看這種情況,這部分可以說明在某些情況下雖然我們最終正確顯示了結果,但其間仍然進行了不正確的轉換。
1)能夠正確顯示的中間不正確轉換
我們知道StringGBKByteArrayGBKString是正確的,但如果我們採用StringGBKByteArrayISO-8859-1String呢?通過測試結果如下:
StringGBKByteArrayISO-8859-1String:/u0061/u4E2D/u6587(a中文)-〉0x61 0xD6 0xD0 0xCE 0xC4-〉/u0061/u00D6/u00D0/u00CE/u00C4(a????)
這時我們得到的字串為?亂碼“a????”,但是通過繼續轉換我們仍然可以復原回正確的字串“a中文”,過程如下:
StringGBKByteArrayISO-8859-1StringISO-8859-1ByteArrayGBKString
對應:/u0061/u4E2D/u6587(a中文)-〉0x61 0xD6 0xD0 0xCE 0xC4-〉/u0061/u00D6/u00D0/u00CE/u00C4(a????)-〉0x61 0xD6 0xD0 0xCE 0xC4-〉/u0061/u4E2D/u6587(a中文)
也就是我們在首次構造字串時,我們用了錯誤的編碼集合得到了錯誤的亂碼,但是我們通過錯上加錯,再用錯誤的編碼集合獲取位元組陣列,然後再用正確的編碼集合構造,就又恢復了正確的字串。這時就屬於是“能夠正確顯示的中間不正確轉換”。在Jsp頁面提交資料處理時常常發生這種情況。
此外能夠正確顯示的中間不正確轉換還有:
StringUTF-8ByteArrayISO-8859-1StringISO-8859-1ByteArrayUTF-8String
StringUTF-8ByteArrayGBKStringGBKByteArrayUTF-8String
對應:/u0061/u4E2D/u6587(a中文)-〉0x61 0xE4 0xB8 0xAD 0xE6%0x96 0x87-〉/u0061/u6D93/uE15F/u6783(a涓枃)-〉0x61 0xE4 0xB8 0xAD 0xE6%0x96 0x87-〉/u0061/u4E2D/u6587(a中文)
4、編碼過程中錯誤診斷參考
1)一個漢字對應一個問號
在通過ISO-8859-1從字串獲取位元組陣列時,由於一個Unicode轉換成一個byte,當遇到不認識的Unicode時,轉換為0x3F,這樣無論用哪種編碼構造時都會產生一個?亂碼。
2)一個漢字對應兩個問號
在通過GBK從字串獲取位元組陣列時,由於一個Unicode轉換成兩個byte,如果此時用ISO-8859-1或用UTF-8構造字串就會出現兩個問號。
若是通過ISO-8859-1構造可以再通過上面所說的錯上加錯恢復(即再通過從ISO-8859-1解析,用GBK構造);
若是通過UTF-8構造則會產生Unicode字元"/uFFFD",不能恢復,若再通過String-UTF-8〉ByteArray-GBK〉String,則會出現雜碼,如a錕斤拷錕斤拷
3)一個漢字對應三個問號
在通過UTF-8從字串獲取位元組陣列時,由於一個
 

    這是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"(兩個問號)。

java   .class類的編碼為:unicode;

windows 預設的編碼為:中文:gb2312; 英文:iso8859;

String str = "張三" ;

byte[] jiema= str.getBytes("gb2312") ; //解碼

String   bianma = new String(jiema,"UTF-8");//編碼 如果上面的解碼不對 可能出現問題

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. 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,則是指定輸出內容的編碼,同時,該設定會傳遞給瀏覽器,告訴瀏覽器輸出內容所採用的編碼。

4. 處理過程

    下面分析兩個有代表性的例子,說明java對編碼有關問題的處理方法。

   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 在頁面中指定編碼是個好習慣,否則可能失去控制,無法指定正確的編碼。

    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.

5. 幾處設定

    對於web應用程式,和編碼有關的設定或者函式如下。

    5.1. jsp編譯

    指定檔案的儲存編碼,很明顯,該設定應該置於檔案的開頭。例如:。另外,對於一般class檔案,可以在編譯的時候指定編碼。

    5.2. jsp輸出

    指定檔案輸出到browser是使用的編碼,該設定也應該置於檔案的開頭。例如:。該設定和response.setCharacterEncoding("GBK")等效。

    5.3. meta設定

    指定網頁使用的編碼,該設定對靜態網頁尤其有作用。因為靜態網頁無法採用jsp的設定,而且也無法執行response.setCharacterEncoding()。例如:

    如果同時採用了jsp輸出和meta設定兩種編碼指定方式,則jsp指定的優先。因為jsp指定的直接體現在response中。

    需要注意的是,apache有一個設定可以給無編碼指定的網頁指定編碼,該指定等同於jsp的編碼指定方式,所以會覆蓋靜態網頁中的meta指定。所以有人建議關閉該設定。

   5.4. form設定

    當瀏覽器提交表單的時候,可以指定相應的編碼。例如:。一般不必不使用該設定,瀏覽器會直接使用網頁的編碼。

相關文章