中文亂碼問題分析Java_第1頁
中文亂碼問題分析Java_第2頁
中文亂碼問題分析Java_第3頁
中文亂碼問題分析Java_第4頁
中文亂碼問題分析Java_第5頁
已閱讀5頁,還剩3頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、第一章 基礎知識1.1 介紹1.1.1 iso8859-1 屬于單字節編碼,最多能表示的字符范圍是0-255,應用于英文系列。比如,字母a的編碼為0x61=97。ISO 8859-1 ,即俗稱的latin-1,西歐字母。其中前半段是ASCII碼。很明顯,iso8859-1編碼表示的字符范圍很窄,無法表示中文字符。但是,由于是單字節編碼,和計算機最基礎的表示單位一致,所以很多時候,仍舊使用iso8859-1編碼來表示。而且在很多協議上,默認使用該編碼。比如,雖然"中文"兩個字不存在iso8859-1編碼,以gb2312編碼為例,應該是"d6d0 cec4"

2、兩個字符,使用iso8859-1編碼的時候則將它拆開為4個字節來表示:"d6 d0 ce c4"(事實上,在進行存儲的時候,也是以字節為單位處理的)。而如果是UTF編碼,則是6個字節"e4 b8 ad e6 96 87"。很明顯,這種表示方法還需要以另一種編碼為基礎。1.1.2 GB系列 GB系列這就是漢子的國標碼,專門用來表示漢字,是雙字節編碼,而英文字母和iso8859-1一致(兼容iso8859-1編碼)。其中gbk編碼能夠用來同時表示繁體字和簡體字,而gb2312只能表示簡體字,gbk是兼容gb2312編碼的。 GB2312-80 是在

3、國內計算機漢字信息技術發展初始階段制定的,其中包含了大部分常用的一、二級漢字,和 9 區的符號。該字符集是幾乎所有的中文系統和國際化的軟件都支持的中文字符集,這也是最基本的中文字符集。其編碼范圍是高位0xa10xfe,低位也是 0xa1-0xfe;漢字從 0xb0a1 開始,結束于 0xf7fe;GBK 是 GB2312-80 的擴展,是向上兼容的。它包含了 20902 個漢字,其編碼范圍是 0x8140-0xfefe,剔除高位 0x80 的字位。其所有字

4、符都可以一對一映射到 Unicode 2.0,也就是說 JAVA 實際上提供了 GBK 字符集的支持。這是現階段 Windows 和其它一些中文操作系統的缺省字符集,但并不是所有的國際化軟件都支持該字符集,感覺是他們并不完全知道 GBK 是怎么回事。值得注意的是它不是國家標準,而只是規范。隨著 GB18030-2000國標的發布,它將在不久的將來完成它的歷史使命。GB18030-2000(GBK2K) 在 GBK 的基礎上進一步擴展了漢字,增加了藏、蒙等少

5、數民族的字形。GBK2K 從根本上解決了字位不夠,字形不足的問題。它有幾個特點:1、 它并沒有確定所有的字形,只是規定了編碼范圍,留待以后擴充。 2、 編碼是變長的,其二字節部分與 GBK 兼容;四字節部分是擴充的字形、字位,其編碼范圍是首字節 0x81-0xfe、二字節0x30-0x39、三字節 0x81-0xfe、四字節0x30-0x39。3、 它的推廣是分階段的,首先要求實現的是能夠完全映射到 Unicode 3.0 標準的所有字形。 4、 它是國家標準,是強制性的。 5、 現在

6、還沒有任何一個操作系統或軟件實現了 GBK2K 的支持,這是現階段和將來漢化的工作內容1.1.3 unicode 這是最統一的編碼,可以用來表示所有語言的字符,而且是定長雙字節(也有四字節的)編碼,包括英文字母在內。所以可以說它是不兼容iso8859-1編碼的,也不兼容任何編碼。不過,相對于iso8859-1編碼來說,uniocode編碼只是在前面增加了一個0字節,比如字母a為"00 61"。需要說明的是,定長編碼便于計算機處理(注意GB2312/GBK不是定長編碼),而unicode又可以用來表示所有字符,所以在很多軟件內部是使用unicode編碼來處

7、理的,比如java。 ISO 10646, 即通用字符集(Universal Character Set, UCS),四個字節編碼UCS 標準 (ISO 10646) 描述了一個 31 位字符集的體系, 不過, 目前只使用了前面 65534個編碼位置 (0x0000-0xfffd, 它們被稱為 基本多語言塊 (Basic Multilingual Plane,BMP), 分配給了字符, 而且我們估計只有那些很古怪的字符(比如Hieroglyphics)為了專門的科學目的, 才會在將來的某個時候, 需要 16 位的 BMP 之外的部分. Unicode編碼頭256個字符和ISO8559-1一樣。

8、前面補上空字節。UniHAN主要分布在U3400到U9FFF之間,此外,UF900到UFAFF之間也有一些。事實上,GB2312和BIG5的字符都分布在U4E00和U9FFF之間。Unicode中中文:4e00-9fa51.1.4 UTF 考慮到unicode編碼不兼容iso8859-1編碼,而且容易占用更多的空間:因為對于英文字母,unicode也需要兩個字節來表示。所以unicode不便于傳輸和存儲。因此而產生了utf編碼,utf編碼兼容iso8859-1編碼,同時也可以用來表示所有語言的字符,不過,utf編碼是不定長編碼,每一個字符的長度從1-6個字節不等。另外,utf編碼自帶簡單的校驗

9、功能。一般來講,英文字母都是用一個字節表示,而漢字使用三個字節。 注意,雖然說utf是為了使用更少的空間而使用的,但那只是相對于unicode編碼來說,如果已經知道是漢字,則使用GB2312/GBK無疑是最節省的。不過另一方面,值得說明的是,雖然utf編碼對漢字使用3個字節,但即使對于漢字網頁,utf編碼也會比unicode編碼節省,因為網頁中包含了很多的英文字符。上圖是UTF-8中所有三種內碼格式,綠格中的值是固定的,蘭格中是用來存放真正的內碼。1.2 應用系統開發Web系統是遇到字符集問題最多的地方,也非常具有代表性,我們可以發現但凡遇到要進行跨進程間數據交換的時候,都可能會存在字符集的轉

10、換問題,這就導致了我們需要對他進行分析。一個標準的web系統模型如下:這個時候我們就可以看到中間出現了三種的跨進程數據交換。都可能導致通訊不暢,在這個部分的討論中,我們集中于這三種交換的討論。1.2.1 與瀏覽器的交換l 控制瀏覽器瀏覽器提交的數據,包括url和http協議頭部中的內容(包括post上來的數據,cookie等),都會存在字符集的問題,我們在服務器端能影響一部分,但不是全部。Ø 如何影響頁面展現和Post方法提交時自動進行的數據編碼。在web系統開發的時候,可以通過response.setContentType()方式直接告訴瀏覽器。或者通過html中嵌入meta的方式

11、告訴瀏覽器。<meta http-equiv="Content-Type" content="text/html;charset=utf-8">但是要注意, 這個 meta 標簽必須放在 head 中靠前面的位置才能保證不會出問題。 因為 Web 服務器讀到這里的時候,就會停止解析,然后用讀到的這個編碼方式重新解析頁面。Ø 瀏覽器的url編碼受本地字符集(一般中文windows為GBK)和瀏覽器設置(ie6中有個設置是總用utf-8發送url的選項)注意:這個設置對參數無效,只對URL的前半部分有效,也就是說,url=”中文一.js

12、p?para1=中文二”,這樣的一個url,在發送的時候,只有【中文一】會被用utf-8編碼,而【中文二】依然會使用本地字符集進行編碼,對于中文操作系統來說,也就是GBK。Ø Cookie這個部分在存儲的時候就已經是編好碼的,瀏覽器在發送的時候并不參與編碼的過程,所以這個部分是完全可控的。l 解析瀏覽器提交的數據對于瀏覽器提交的數據,在網絡上以字節流的方式發送到服務器端,服務器在進行解析的時候必須知道提交的字符集是什么。一般的編程環境都提供對字節流的解析方式的設置,如java提供request.setCharacterEncoding方式。當然在早期的環境很多沒有很好的國際化支持,而

13、且,各種web服務器的實現也影響解析的過程,在實戰中需要具體問題具體分析。1.2.2 與數據庫的交換數據庫在構建時已經設置好了字符集,不會受應用程序的影響,無論讀寫操作,都需要尊重數據庫的設置。我們能做的,只是在開發時,告訴應用程序,數據庫的字符集是什么以便應用程序能正確識別數據。現在的數據庫,一般可以在driver的連接上設置字符集,由封裝好的driver包來控制字符的編碼與解碼。比如mysql的jdbc連接串:mysql.url=jdbc:mysql:/localhost/acai?useUnicode=true&characterEncoding =8859_11.2.

14、3 與文件的交換與文件的交換,為讀寫兩個部分。l 讀文件讀文件時,必須告訴應用程序,將要讀取的文件的字符集編碼,從邏輯上來說這是很清晰的,但在實戰中有問題出現了:Ø 為了編程方便,大多開發環境都支持default字符集,也就是在開發時無需到處指定,這時由于運行環境的變化,就會導致莫名的錯誤。Ø 文件所采用的字符集很難識別,由于各種便利性工具的出現,使得我們日常編寫文件時越來越少的關注文件的字符集。導致經常性的錯誤出現。l 寫文件Ø 寫文件相對簡單,將輸出流指定字符集,如果沒有指定,就會按照默認的字符集進行處理。1.2.4 總結由于java或者其他高級語言本身會做一

15、次從字節流到字符串的轉換,將字節流以Unicode方式存儲,所以編碼問題變得重要起來。邏輯上,應用程序讀到字節流然后按照你設定的字符集進行識別,并在內存中將此種識別結果自動存為Unicode編碼方式,應用程序并不管是什么內容,但一定知道這個字符的Unicode的碼值。從邏輯上來說,我們只要在字符轉換動作發生之前先為程序設定字符集就可以了。然而上面的討論都只是理想中的狀況,問題是就只在程序這端我們依然不能獲得完全的控制權。第二章 具體開發場景前面提及的內容都是理想情況下的,在實戰中,由于各種環境實現的差異(如web容器,JDK版本,操作系統環境),實際遇到問題時還是需要具體情況具體分析。2.1

16、Java開發對于Java來說,所有的字符串,在內存中總是以UniCode存儲的,注意是Unicode而不是utf-8。2.1.1 字符串的處理l getBytes(charset) 這是java字符串處理的一個標準函數,其作用是將字符串所表示的字符按照charset編碼,并以字節方式表示。注意字符串在java內存中總是按unicode編碼存儲的。比如"中文",正常情況下(即沒有錯誤的時候)存儲為"4e2d 6587",如果charset為"gbk",則被編碼為"d6d0 cec4",然后返回字節"d6 d

17、0 ce c4"。如果charset為"utf8"則最后是"e4 b8 ad e6 96 87"。如果是"iso8859-1",則由于無法編碼,最后返回 "3f 3f"(兩個問號)。l new String(charset) 這是java字符串處理的另一個標準函數,和上一個函數的作用相反,將字節數組按照charset編碼進行組合識別,最后轉換為unicode存儲。參考上述getBytes的例子,"gbk" 和"utf8"都可以得出正確的結果"4e2d 65

18、87",但iso8859-1最后變成了"003f 003f"(兩個問號)。 因為utf8可以用來表示/編碼所有字符,所以new String( str.getBytes( "utf8" ), "utf8" ) 等于 str,即完全可逆。l 其他處理java.io包中很多關于“字符串輸入輸出”的類,包括Reader,InputStream,OutputStream等,都有相應的接口可以設置字符集。2.1.2 Web容器Java的web端需要用到web容器,我們很多的數據都是從Web容器中來的,最常用的有:1、 request.

19、getParameter()系列。2、 request.getRequestURI()3、 request.getServletPath()4、 request.getHeader()5、 request.getCookies()6、 。在我們拿到這些數據的時候已經是字符串了,如果編碼出問題,就已經出了,所以我們要提前干預。1、setCharacterEncoding在servlet2.3規范中,專門為request對象增加了一個setCharacterEncoding()方法,在執行任何獲取parameter相關的操作之前,設置好,就可以自定義對參數解碼的字符集,同時也是在2.3規范中支持了

20、Filter屬性,以便于能在一個地方進行控制。2.1.3 程序編譯Java的class文件和內存中都是以Unicode形態存在,但是java源文件,jsp源文件卻不一定,這個時候也可能出現編碼問題。所以如果出現默認編譯字符集和文件字符集不相同的情況,就需要在編譯時加上-encoding選項。對于java文件,直接在javac后增加參數-encoding就行,對于在IDE中開發編譯的情況,一般可以不用關心。對于JSP編譯,有兩種方式解決這種字符集差異:l 指定文件的存儲編碼。很明顯,該設置應該置于文件的開頭。例如:<%page pageEncoding="GBK"%&g

21、t;。l 經過測試,tomcat對jsp的編譯除了受pageEncoding參數影響,還會受content-type影響。pageEncoding的優先級大于contenttype。構建一個最簡單的jsp如下,注意,此時中文在實際存儲時對應的字節碼應該為D6D0CEC4。<% page language="java" pageEncoding="GBK"%><% page contentType="text/html; charset=GBK"%>中文我們用ultraEdit去修改上述文件測試,再結合tomca

22、t編程生成的java文件對比,可以得出上述結論。l 對于一些web容器可以設置相應的參數,來指定jsp源文件使用的字符集,如websphere6中對源文件字符集的設置為:-DFILE encoding = GBK2.1.4 JDK版本差異JDK的不同版本在處理字符時也有差異。tryString hex = "D632"String str = new String(hex2byte(hex),"GBK");System.out.println(str);catch(Exception ex)ex.printStackTrace();上述代碼的功能是:將十

23、六進制表示為“D632”的字節流按照GBK編碼轉換為一個字符串,hex2byte方法是將16進制描述的字符串轉換為對應的byte數組。在JDK1.5及以前版本和JDK1.6版本中反映的結果不一致,“D632”這個16進制的雙字節字符在GBK里并沒有對應的字符。環境顯示JDK1.5?2JDK1.6?中文windows?Oracle?這個小差異,是在一次異構數據庫遷移的時候發現的,當時我們有效利用了JDK1.5的這種特殊性,為數據遷移做了一個工具,用于識別遷移數據中出現的中文被截斷的問題。2.1.5 JAVA 的encodingjava支持的encoding中與中文編程相關的有:(有幾個

24、在JDK文檔中未列出)1、 ASCII 7-bit, 同 ascii72、 ISO8859-1 8-bit, 同 8859_1,ISO-8859-1,ISO_8859-1,latin1. 3、 GB2312-80 同gb2312,gb2312-1980,EUC_CN,euccn,1381,Cp1381,1383, Cp1383, ISO2022CN,ISO2022CN_GB. 4、 GBK (注意大小寫),同MS936 5、 UTF8 UTF-8 6、 GB18030 , 同Cp1392,1392 2.2 tomcat設置2.2.1 url中的中文URL中包含中文(或者是get方法提交的數據中有中文),這個問題總是比較麻煩,需要服務器端和客戶端保持一致。 一般情況下,在tomcat的server.

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論