1. 程式人生 > >String.getBytes()與下載時編碼為iso8859-1的詳解

String.getBytes()與下載時編碼為iso8859-1的詳解

有時候,為了讓中文字元適應某些特殊要求(如http header頭要求其內容必須為iso8859-1編碼),可能會通過將中文字元按照位元組方式來編碼的情況,如 

String s_iso88591 = new String("中".getBytes("UTF-8"),"ISO8859-1"), 

這樣得到的s_iso8859-1字串實際是三個在 ISO8859-1中的字元,在將這些字元傳遞到目的地後, 

目的地程式再通過相反的方式String s_utf8 = new String(s_iso88591.getBytes("ISO8859-1"),"UTF-8")來得到正確的中文漢字“中”。這樣就既保證了遵守協議規定、也支援中文。 


在Java中,String的getBytes()方法是得到一個作業系統預設的編碼格式的位元組陣列。這個表示在不通OS下,返回的東西不一樣! 



String.getBytes(String decode)方法會根據指定的decode編碼返回某字串在該編碼下的byte陣列表示,如 

byte[] b_gbk = "中".getBytes("GBK"); 
byte[] b_utf8 = "中".getBytes("UTF-8"); 
byte[] b_iso88591 = "中".getBytes("ISO8859-1"); 


將分別返回“中”這個漢字在GBK、UTF-8和ISO8859-1編碼下的byte陣列表示,此時b_gbk的長度為2,b_utf8的長度為3,b_iso88591的長度為1。 


而與getBytes相對的,可以通過new String(byte[], decode)的方式來還原這個“中”字時,這個new String(byte[], decode)實際是使用decode指定的編碼來將byte[]解析成字串。 

String s_gbk = new String(b_gbk,"GBK"); 
String s_utf8 = new String(b_utf8,"UTF-8"); 
String s_iso88591 = new String(b_iso88591,"ISO8859-1"); 

通過列印s_gbk、s_utf8和s_iso88591,會發現,s_gbk和s_utf8都是“中”,而只有s_iso88591是一個不認識的字元,為什麼使用ISO8859-1編碼再組合之後,無法還原“中”字呢,其實原因很簡單,因為ISO8859-1編碼的編碼表中,根本就沒有包含漢字字元,當然也就無法通過"中".getBytes("ISO8859-1");來得到正確的“中”字在ISO8859-1中的編碼值了,所以再通過new String()來還原就無從談起了。 


因此,通過String.getBytes(String decode)方法來得到byte[]時,一定要確定decode的編碼表中確實存在String表示的碼值,這樣得到的byte[]陣列才能正確被還原。 

有時候,為了讓中文字元適應某些特殊要求(如http header頭要求其內容必須為iso8859-1編碼),可能會通過將中文字元按照位元組方式來編碼的情況,如 

String s_iso88591 = new String("中".getBytes("UTF-8"),"ISO8859-1"), 

這樣得到的s_iso8859-1字串實際是三個在 ISO8859-1中的字元,在將這些字元傳遞到目的地後, 

目的地程式再通過相反的方式String s_utf8 = new String(s_iso88591.getBytes("ISO8859-1"),"UTF-8")來得到正確的中文漢字“中”。這樣就既保證了遵守協議規定、也支援中文。 


=================================================== 

JAVA編碼轉換的詳細過程 

我們常見的JAVA程式包括以下類別: 
       *直接在console上執行的類(包括視覺化介面的類) 
       *JSP程式碼類(注:JSP是Servlets類的變型) 
       *Servelets類 
       *EJB類 
       *其它不可以直接執行的支援類 
這些類檔案中,都有可能含有中文字串,並且我們常用前三類JAVA程式和使用者直接互動,用於輸出和輸入字元,如:我們在JSP和Servlet中得到客戶端送來的字元,這些字元也包括中文字元。無論這些JAVA類的作用如何,這些JAVA程式的生命週期都是這樣的: 
*程式設計人員在一定的作業系統上選擇一個合適的編輯軟體來實現源程式程式碼並以.java副檔名儲存在作業系統中,例如我們在中文win2k中用記事本編輯一個java源程式; 
       *程式設計人員用JDK中的javac.exe來編譯這些原始碼,形成.class類(JSP檔案是由容器呼叫JDK來編譯的); 
       *直接執行這些類或將這些類佈署到WEB容器中去執行,並輸出結果。 
      那麼,在這些過程中,JDK和JVM是如何將這些檔案如何編碼和解碼並執行的呢? 
      這裡,我們以中文win2k作業系統為例說明JAVA類是如何來編碼和被解碼的。 
第一步,我們在中文win2k中用編輯軟體如記事本編寫一個Java源程式檔案(包括以上五類JAVA程式),程式檔案在儲存時預設採用了作業系統預設支援GBK編碼格式(作業系統預設支援的格式為file.encoding格式)形成了一個.java檔案,也即,java程式在被編譯前,我們的JAVA源程式檔案是採用作業系統預設支援的file.encoding編碼格式儲存的,java源程式中含有中文資訊字元和英文程式程式碼;要檢視系統的file.encoding引數,可以用以下程式碼: 
public class ShowSystemDefaultEncoding { 
public static void main(String[] args) { 
String encoding = System.getProperty("file.encoding"); 
System.out.println(encoding); 
}} 
第二步,我們用JDK的javac.exe檔案編譯我們的Java源程式,由於JDK是國際版的,在編譯的時候,如果我們沒有用-encoding引數指定我們的JAVA源程式的編碼格式,則javac.exe首先獲得我們作業系統預設採用的編碼格式,也即在編譯java程式時,若我們不指定源程式檔案的編碼格式,JDK首先獲得作業系統的file.encoding引數(它儲存的就是作業系統預設的編碼格式,如WIN2k,它的值為GBK),然後JDK就把我們的java源程式從file.encoding編碼格式轉化為JAVA內部預設的UNICODE格式放入記憶體中。然後,javac把轉換後的unicode格式的檔案進行編譯成.class類檔案,此時.class檔案是UNICODE編碼的,它暫放在記憶體中,緊接著,JDK將此以UNICODE編碼的編譯後的class檔案儲存到我們的作業系統中形成我們見到的.class檔案。對我們來說,我們最終獲得的.class檔案是內容以UNICODE編碼格式儲存的類檔案,它內部包含我們源程式中的中文字串,只不過此時它己經由file.encoding格式轉化為UNICODE格式了。這一步中,對於JSP源程式檔案是不同的,對於JSP,這個過程是這樣的:即WEB容器呼叫JSP編譯器,JSP編譯器先檢視JSP檔案中是否設定有檔案編碼格式,如果JSP檔案中沒有設定JSP檔案的編碼格式,則JSP編譯器呼叫JDK先把JSP檔案用JVM預設的字元編碼格式(也即WEB容器所在的作業系統的預設的file.encoding)轉化為臨時的Servlet類,然後再把它編譯成UNICODE格式的class類,並儲存在臨時資料夾中。如:在中文win2k上,WEB容器就把JSP檔案從GBK編碼格式轉化為UNICODE格式,然後編譯成臨時儲存的Servlet類,以響應使用者的請求。 
      第三步,執行第二步編譯出來的類,分為三種情況: 
      A、 直接在console上執行的類 
      B、 EJB類和不可以直接執行的支援類(如JavaBean類) 
      C、 JSP程式碼和Servlet類 
      D、 JAVA程式和資料庫之間 
下面我們分這四種情況來看。 
A、直接在console上執行的類 
這種情況,執行該類首先需要JVM支援,即作業系統中必須安裝有JRE。執行過程是這樣的:首先java啟動JVM,此時JVM讀出作業系統中儲存的class檔案並把內容讀入記憶體中,此時記憶體中為UNICODE格式的class類,然後JVM執行它,如果此時此類需要接收使用者輸入,則類會預設用file.encoding編碼格式對使用者輸入的串進行編碼並轉化為unicode儲存入記憶體(使用者可以設定輸入流的編碼格式)。程式執行後,產生的字串(UNICODE編碼的)再回交給JVM,最後JRE把此字串再轉化為file.encoding格式(使用者可以設定輸出流的編碼格式)傳遞給作業系統顯示介面並輸出到介面上。以上每一步的轉化都需要正確的編碼格式轉化,才能最終不出現亂碼現象。 
B、EJB類和不可以直接執行的支援類(如JavaBean類) 
由於EJB類和不可以直接執行的支援類,它們一般不與使用者直接互動輸入和輸出,它們常常與其它的類進行互動輸入和輸出,所以它們在第二步被編譯後,就形成了內容是UNICODE編碼的類儲存在作業系統中了,以後只要它與其它的類之間的互動在引數傳遞過程中沒有丟失,則它就會正確的執行。 
C、JSP程式碼和Servlet類 
經過第二步後,JSP檔案也被轉化為Servlets類檔案,只不過它不像標準的Servlets一校存在於classes目錄中,它存在於WEB容器的臨時目錄中,故這一步中我們也把它做為Servlets來看。 
對於Servlets,客戶端請求它時,WEB容器呼叫它的JVM來執行Servlet,首先,JVM把Servlet的class類從系統中讀出並裝入記憶體中,記憶體中是以UNICODE編碼的Servlet類的程式碼,然後JVM在記憶體中執行該Servlet類,如果Servlet在執行的過程中,需要接受從客戶端傳來的字元如:表單輸入的值和URL中傳入的值,此時如果程式中沒有設定接受引數時採用的編碼格式,則WEB容器會預設採用ISO-8859-1編碼格式來接受傳入的值並在JVM中轉化為UNICODE格式的儲存在WEB容器的記憶體中。Servlet執行後生成輸出,輸出的字串是UNICODE格式的,緊接著,容器將Servlet執行產生的UNICODE格式的串(如html語法,使用者輸出的串等)直接傳送到客戶端瀏覽器上並輸出給使用者,如果此時指定了傳送時輸出的編碼格式,則按指定的編碼格式輸出到瀏覽器上,如果沒有指定,則預設按ISO-8859-1編碼傳送到客戶的瀏覽器上。 
D、Java程式和資料庫之間 
對於幾乎所有資料庫的JDBC驅動程式,預設的在JAVA程式和資料庫之間傳遞資料都是以ISO-8859-1為預設編碼格式的,所以,我們的程式在向資料庫記憶體儲包含中文的資料時,JDBC首先是把程式內部的UNICODE編碼格式的資料轉化為ISO-8859-1的格式,然後傳遞到資料庫中,在資料庫儲存資料時,它預設即以ISO-8859-1儲存,所以,這是為什麼我們常常在資料庫中讀出的中文資料是亂碼。