談談我對Java中Unicode、編碼的理解
我們經常會遇到編碼問題。Java號稱國際化的語言,是因為它的class檔案採用UTF-8,而JVM運行時使用UTF-16(至於為什麼JVM中要採用UTF-16,我沒看過 相關的資料,但我猜可能是因為JAVA裡面一個字元(char)就是16位的,而UTF-16正是雙位元組編碼),都是unicode的編碼。
unicode 的目標就是能支援世界上所有的字元集,也就是說幾乎所有的字元集包含的字元在unicode中都有對應的編碼。在unicode中,字元與代碼的映射關 系,就是unicode字元集,稱為UCS(Unicode Character Set),每個unicode字元編碼稱為code point(代碼點?)。UTF-8和UTF-16是不同的UCS編碼方法,UTF就是UCS Transformation Format。;
在Java 中,String的getBytes()方法就是對特定的字串(unicode)按照給定的字元集進行編碼(encode),new String()則可以按照某個字元集將位元組流轉換回unicode(decode)。Java裡面的每一個String都是unicode編碼。
再來看頁面,如果不做特殊處理,Form的提交就按照頁面的ContentType設定中的字元集進行編碼轉換,發送到後台,後台必須利用req.setCharacterEncoding來指定參數的編碼格式(不同的應用伺服器應有不同的指定方式),才能正確解碼。
Java 裡面的encode和decode都是相對於unicode而言的,encode的意思是將char[] --> XXX Encoding byte[],decode就是由XXX Encoding byte[] --> char[]。平常,當我們說“將GBK編碼轉換為UTF-8編碼”的時候,實際的意思就是:GBK Encoding byte[] --> UTF-8 Encoding byte[],這種轉換隻有在需要用byte[]傳輸資料的時候才有意義,否則便是毫無意義的。
首先要說明的一點是:Java中的String對象就是一個unicode編碼的字串。
但是,我們通常會聽到有人說:“我們需要將String由ISO-8859-1轉換為GBK編碼”,這又是怎麼回事呢?實際上,我們並不是要“將 一個由ISO-8859-1編碼的String轉換為GBK編碼的String”,反覆說明的是,JAVA中的String都是unicode編碼的,所以不存在“ISO- 8859-1編碼的String”或“GBK編碼的String”這樣的說法。而需要轉換的唯一的原因是String進行了錯誤的編碼。我們經常會碰到由ISO-8859- 1轉換為諸如GBK/UTF-8等等這樣的需求。所謂的轉換過程是:String --> byte[] -->String。
也許 你非常清楚這個過程的代碼:new String(text.getBytes("ISO-8859-1"),"GBK")。但是,要真正理解起來並不是那麼簡單。表面上看似乎很容易理解, 不就是將text String對象按照ISO-8859-1的方式編碼為byte[]然後再把它按照GBK的方式轉換為String嗎?但是這句代碼很容易會被誤解為: “將text String由ISO-8859-1轉換為GBK編碼”,這種說法是錯誤的。難道你見過用這樣的代碼:new String(text.getBytes("GBK"),"UTF-8")來對String進行編碼轉換的嗎?
之所以你會經常看到new String(text.getBytes("ISO-8859-1"),"GBK")這句代碼,是因為一個GBK的位元組流被錯誤地以ISO-8859- 1的方式轉換為String(unicode)了!發生這種情況最普遍的地方是一個GBK編碼的網頁向後台提交資料的時候,就有可能會看到這句代碼的出 現。GBK的流被錯誤的當成ISO8859-1的流,所以便得到了一個錯誤的String。由於ISO8859-1是單位元組編碼,所以每個位元組被按照原樣 轉換為String,也就是說,雖然這是一個錯誤的轉換,但編碼沒有改變,所以我們仍然有機會把編碼轉換回來!所以那句經典的new String(text.getBytes("ISO-8859-1"),"GBK")便出現了。
如果系統誤以為是其它編碼格式,就有可能再也轉換不回來了,因為編碼轉換並不是負負得正那麼簡單的