Java中文亂碼問題產生原因分析_第1頁
Java中文亂碼問題產生原因分析_第2頁
Java中文亂碼問題產生原因分析_第3頁
Java中文亂碼問題產生原因分析_第4頁
Java中文亂碼問題產生原因分析_第5頁
已閱讀5頁,還剩3頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1、Java中文亂碼問題產生原因分析在計算機中,只有二進制的數據,不管數據是在內存中,還是在外部存儲設備上。對于我們所看到的 字符,也是以二進制數據的形式存在的。不同字符對應二進制數的規(guī)則,就是字符的編碼。字符編碼的集 合稱為字符集。17.1.1常用字符集在早期的計算機系統(tǒng)中,使用的字符非常少,這些字符包括26個英文字母、數字符號和一些常用符 號(包括控制符號),對這些字符進行編碼,用1個字節(jié)就足夠了(1個字節(jié)可以表示28=256種字符)。然 而實際上,表示這些字符,只使用了 1個字節(jié)的7位,這就是ASCII編碼。ASCIIASCII (American Standard Code for Inf

2、ormation Interchange,美國信息互換標準代碼),是基于常用的 英文字符的一套電腦編碼系統(tǒng)。每一個ASCII碼與一個8位(bit)二進制數對應。其最高位是0,相應的 十進制數是0127。例如,數字字符“0”的編碼用十進制數表示就是48。另有128個擴展的ASCII碼, 最高位都是1,由一些圖形和畫線符號組成。ASCII是現今最通用的單字節(jié)編碼系統(tǒng)。ASCII用一個字節(jié)來表示字符,最多能夠表示256種字符。隨著計算機的普及,許多國家都將本地的 語言符號引入到計算機中,擴展了計算機中字符的范圍,于是就出現了各種不同的字符集。ISO8859-1因為ASCII碼中缺少、u和許多書寫其他

3、語言所需的字符,為此,可以通過指定128以后的字符來 擴展ASCII碼。國際標準組織(ISO)定義了幾個不同的字符集,它們是在ASCII碼基礎上增加了其他語 言和地區(qū)需要的字符。其中最常用的是ISO8859-1,通常叫做Latin-1。Latin-1包括了書寫所有西方歐洲語 言不可缺少的附加字符,其中0127的字符與ASCII碼相同。ISO 8859另外定義了 14個適用于不同文字 的字符集(8859-2到8859-15)。這些字符集共享0127的ASCII碼,只是每個字符集都包含了 128255 的其他字符。GB2312 和 GBKGB2312是中華人民共和國國家標準漢字信息交換用編碼,全稱

4、信息交換用漢字編碼字符集一基本 集,標準號為GB2312-80,是一個由中華人民共和國國家標準總局發(fā)布的關于簡化漢字的編碼,通行于 中國大陸和新加坡,簡稱國標碼。因為中文字符數量較多,所以采用兩個字節(jié)來表示一個字符,分別稱為高位和低位。為了和ASCII碼 有所區(qū)別,中文字符的每一個字節(jié)的最高位都用1來表示。GB2312字符集是幾乎所有的中文系統(tǒng)和國際 化的軟件都支持的中文字符集,也是最基本的中文字符集。它包含了大部分常用的一、二級漢字和9區(qū)的 符號,其編碼范圍是高位0 xa1-0 xfe,低位也是0 xa1-0 xfe,漢字從0 xb0a1開始,結束于0 xf 7fe。為了對更多的字符和符號進

5、行編碼,由前電子部科技質量司和國家技術監(jiān)督局標準化司于1995年12 月頒布了 GBK(K是“擴展”的漢語拼音第一個字母)編碼規(guī)范,在新的編碼系統(tǒng)里,除了完全兼容GB2312 外,還對繁體中文、一些不常用的漢字和許多符號進行了編碼。它也是現階段Windows和其他一些中文操 作系統(tǒng)的默認字符集,但并不是所有的國際化軟件都支持該字符集。不過要注意的是GBK不是國家標準, 它只是規(guī)范。GBK字符集包含了 20 902個漢字,其編碼范圍是0 x8140-0 xfefe。每個國家(或區(qū)域)都規(guī)定了計算機信息交換用的字符編碼集,這就造成了交流上的困難。想像一下, 你發(fā)送一封中文郵件給一位遠在西班牙的朋友

6、,當郵件通過網絡發(fā)送出去的時候,你所書寫的中文字符會 按照本地的字符集GBK轉換為二進制編碼數據,然后發(fā)送出去。當你的朋友接收到郵件(二進制數據) 后,查看信件時,會按照他所用系統(tǒng)的字符集,將二進制編碼數據解碼為字符,然而由于兩種字符集之間 編碼的規(guī)則不同,導致轉換出現亂碼。這是因為,在不同的字符集之間,同樣的數字可能對應了不同的符 號,也可能在另一種字符集中,該數字沒有對應符號。為了解決上述問題,統(tǒng)一全世界的字符編碼,由Unicode協會1制定并發(fā)布了 Unicode編碼。UnicodeUnicode (統(tǒng)一的字符編碼標準集)使用065535的雙字節(jié)無符號數對每一個字符進行編碼。它不僅 包含

7、來自英語和其他西歐國家字母表中的常見字母和符號,也包含來自古斯拉夫語、希臘語、希伯來語、 阿拉伯語和梵語的字母表。另外還包含漢語和日語的象形漢字和韓國的Hangul音節(jié)表。目前已經定義了 40000多個不同的Unicode字符,剩余25000個空缺留給將來擴展使用。其中大約201 Unicode協會是由IBM、微軟、Adobe、SUN、加州大學伯克利分校等公司和組織所組成的非營利性組織。 000個字符用于漢字,另外11000左右的字符用于韓語音節(jié)。Unicode中0255的字符與ISO8859-1中的 一致。Unicode編碼對于英文字符采取前面加“0”字節(jié)的策略實現等長兼容。如“a”的ASC

8、II碼為0 x61, Unicode 碼就為 0 x00, 0 x61。UTF-8使用Unicode編碼,一個英文字符要占用兩個字節(jié),在Internet上,大多數的信息都是用英文來表示 的,如果都采用Unicode編碼,將會使數據量增加一倍。為了減少存儲和傳輸英文字符數據的數據量,可 以使用UTF-8編碼。UTF-8 全稱是 Eight-bit UCS Transformation Format (UCS, Universal Character Set,通用字符集,UCS 是所有其他字符集標準的一個超集)。對于常用的字符,即0127的ASCII字符,UTF-8用一個字節(jié)來表 示,這意味著只包

9、含7位ASCII字符的字符數據在ASCII和UTF-8兩種編碼方式下是一樣的。如果字符對 應的Unicode碼是0 x0000,或在0 x0080與0 x007f之間,對應的UTF-8編碼是兩個字節(jié),如果字符對應的 Unicode碼在0 x0800與0 xffff之間,對應的UTF-8編碼是三個字節(jié)。因為中文字符的Unicode編碼在0 x0800 與0 xffff之間,所以數據如果是中文,采用UTF-8編碼數據量會增加50%。Unicode與UTF-8轉換的規(guī)則簡述如下:如果Unicode編碼的16位二進制數的前9位是0,則UTF-8編碼用1個字節(jié)來表示,這個字節(jié)的 首位是“0”,剩下的7位

10、與原二進制數據的后7位相同。例如:Unicode 編碼:u0061 = 00000000 01100001UTF-8 編碼:01100001 = 0 x61如果Unicode編碼的16位二進制數的頭5位是0,則UTF-8編碼用2個字節(jié)來表示,首字節(jié)以“110” 開頭,后面的5位與原二進制數據除去前5個零后的最高5位相同;第二個字節(jié)以“ 10”開頭,后面的6 位與原二進制數據中的低6位相同。例如:Unicode 編碼:u00A9 = 00000000 10101001UTF-8 編碼:11000010 10101001 = 0 xC2 0 xA9如果不符合上述兩個規(guī)則,則用三個字節(jié)表示。第一個字

11、節(jié)以“ 1110”開頭,后四位為原二進制 數據的高四位;第二個字節(jié)以“10”開頭,后六位為原二進制數據中間的六位;第三個字節(jié)以10”開頭, 后六位為原二進制數據的低六位。例如:Unicode 編碼:u4E2D = 01001110 00101101UTF-8 編碼:11100100 10111000 10101101 = 0 xE4 0 xB8 0 xADI! 注意在UTF-8編碼的多字節(jié)串中,第一個字節(jié)開頭1”的數目就是整個字符串中字節(jié)的數目。17.1.2對亂碼產生過程的分析為了讓使用Java語言編寫的程序能在各種語言的平臺下運行,Java在其內部使用Unicode字符集來表 示字符,這樣就

12、存在Unicode字符集和本地字符集進行轉換的過程。當在Java中讀取字符數據的時候,需 要將本地字符集編碼的數據轉換為Unicode編碼,而在輸出字符數據的時候,則需要將Jnicode編碼轉換為 本地字符集編碼。例如,在中文系統(tǒng)下,從控制臺讀取一個字符“中”,實際上讀取的是“中”的GBK編碼0 xD6D0, 在Java語言中要將GBK編碼轉換為Unicode編碼0 x4E2D,此時,在內存中,字符“中”對應的數值就是 0 x4E2D,當我們向控制臺輸出字符時,Java語言將Unicode編碼再轉換為GBK編碼,輸出到控制臺,中 文系統(tǒng)再根據GBK字符集畫出相應的字符。從上述過程來看,讀取和寫

13、入的過程是可逆的,那么理應不會出現中文亂碼問題。然而,實際應用的 情形,比上述過程要復雜得多。在Web應用中,通常都包括了瀏覽器、Web服務器、Web應用程序和數 據庫等部分,每一部分都有可能使用不同的字符集,從而導致字符數據在各種不同的字符集之間轉換時, 出現亂碼的問題。在Java語言中,不同字符集編碼的轉換,都是通過Unicode編碼作為中介來完成的。例如,GBK編 碼的字符“中”要轉換為ISO-8859-1 (同ISO8859-1)編碼,其過程如下:因為在Java中的字符,都是用Unicode來表示的,所以GBK編碼的字符“中”要轉換為Unicode 表示:0 xD6D0-0 x4E2D

14、。將字符“中”的Unicode編碼轉換為ISO-8859-1編碼,因為Unicode編碼0 x4E2D在ISO-8859-1 中沒有對應的編碼,于是得到0 x3f,也就是字符“?”。下面的代碼演示了這一過程:/GBK編碼的字符“中”轉換為Unicode編碼表示String str=中;/將字符“中”的Unicode編碼轉換為ISO-8859-1編碼byte b=str.getBytes(ISO-8859-1);for(int i=0;ib.length;i+)(/輸出轉換后的二進制代碼。System.out.print(bi);當從Unicode編碼向某個字符集轉換時,如果在該字符集中沒有對應

15、的編碼,則得到0 x3f (即問號字 符?)。這就是為什么有時候我們輸入的是中文,在輸出時卻變成了問號。從其他字符集向Unicode編碼轉換時,如果這個二進制數在該字符集中沒有標識任何的字符,則得到 的結果是0 xfffdo例如一個GBK的編碼值0 x8140,從GB2312向Unicode轉換,然而由于0 x8140不在 GB2312字符集的編碼范圍(0 xa1a1-0 xfefe),當然也就沒有對應任何的字符,所以轉換后會得到0 xfffd。 下面的代碼演示了這一過程。/構造一個二進制數據。byte buf=(byte)0 x81,(byte)0 x40,(byte)0 xb0,(byte

16、)0 xa1;/將二進制數據按照GB2312向Unicode編碼轉換。String str=new String(buf,GB2312);for(int i=0;istr.length();i+)/取出字符串中的每個Unicode編碼的字符。char ch=str.charAt(i);/務該字符對應的Unicode編碼以十六進制的形式輸出。System.out.print(Integer.toHexString(int)ch);System.out.print(-);/輸出該字符。System.out.println(ch);在輸出字符和字符串的時候,會從Unicode編碼向中文系統(tǒng)默認的編碼

17、GBK轉換,由于Unicode編碼 0 xfffd在GBK字符集中沒有對應的編碼,于是得到0 x3f,輸出字符“?”。最后輸出的結果如下:fffd-?40-554a-啊從上述所知,由于存在著多種不同的字符集,在各種字符集之間進行轉換,就有可能出現亂碼,同樣 是中文字符集GB2312和GBK,由于編碼范圍的不同,某些字符在轉換時也會出現亂碼。在一個使用了數據庫的Web應用程序中,亂碼可能會在多個環(huán)節(jié)產生。由于瀏覽器會根據本地系統(tǒng)默 認的字符集來提交數據,而Web容器默認采用的是ISO-8859-1的編碼方式解析POST數據,在瀏覽器提 交中文數據后,Web容器會按照ISO-8859-1字符集來解

18、碼數據,在這一環(huán)節(jié)可能會導致亂碼的產生。由于 大多數數據庫的JDBC驅動程序默認采用ISO-8859-1的編碼方式在Java程序和數據庫之間傳遞數據,我 們的程序在向數據庫中存儲包含中文的數據時,JDBC驅動首先將程序內部的Unicode編碼格式的數據轉 化為ISO-8859-1的格式,然后傳遞到數據庫中,在這一環(huán)節(jié)可能會導致亂碼的產生。目前流行的關系型數 據庫系統(tǒng)都支持數據庫編碼,也就是說在創(chuàng)建數據庫時可以指定它自己的字符集設置,數據庫的數據以指 定的編碼形式存儲。當JDBC驅動向數據庫中保存數據時,有可能還會發(fā)生字符集的轉換。正是由于在 Web應用程序運行過程中,輸入的中文字符需要在不同的

19、字符集之間來回轉換,也就導致了中文亂碼問題 的頻繁出現。圖17-1描述了在Web應用的請求響應過程中,發(fā)生的字符編碼轉換過程,其中瀏覽器是正6.0,Web 容器的是 Tomcat 6.0.16。從圖17-1描述的過程中可以看到,如果在Web應用程序中不指定任何的字符集,從瀏覽器端傳來的 中文字符,輸出回瀏覽器時,可以正常顯示(以簡體中文的方式查看網頁)。然而,事情并沒有這么簡單, 在Servlet/JSP中,可能存在著直接寫入的或從其他來源讀取的中文字符,如果這些字符對應的Unicode碼 是從GB2312編碼轉換而來,那么以ISO-8859-1編碼方式輸出,這些字符將不能正常顯示。所以對于中

20、文 的處理,應該在圖17-1和的位置明確指定使用GB2312或GBK字符集。圖17-1在Web請求響應過程中,中文字符編碼的轉換過程中文亂碼問題的解決方案只要掌握了中文亂碼問題產生的原因,然后對癥下藥,就可以順利地解決這些問題。下面我們對容易 產生亂碼問題的場景進行分析,并提出解決方案。以POST方法提交的表單數據中有中文字符由于Web容器默認的編碼方式是ISO-8859-1,在Servlet/JSP程序中,通過請求對象的getParameter() 方法得到的字符串是以ISO-8859-1轉換而來,這是導致亂碼產生的原因之一。為了避免容器以ISO-8859-1 的編碼方式返回字符串,對于以

21、POST方法提交的表單數據,可以在獲取請求參數值之前,調用 request.setCharacterEncoding(GBK”),明確指定請求正文使用的字符編碼方式是GBK。在向瀏覽器發(fā)送 中文數據之前,調用response.setContentType(text/html;charset=GBK”),指定輸出內容的編碼方式是GBK。對于JSP頁面,在獲取請求參數值之前,寫上下面的代碼:為了指定輸出內容的編碼格式,設置page指令contentType屬性,如下:在Web容器轉換JSP頁面后的Servlet類中,會自動添加下面的代碼:response.setContentType(text/h

22、tml; charset=GBK);以GET方法提交的表單數據中有中文字符當提交表單采用GET方法時,提交的數據作為查詢字符串被附加到URL的末端,發(fā)送到服務器,此 時在服務器端調用setCharacterEncoding()方法也就沒有作用了。我們需要在得到請求參數的值后,自己做 正確的編碼轉換。String name = request.getParameter(name);name=new String(name.getBytes(ISO-8859-1),GBK);在第一行,調用getParameter()方法得到的字符串name的Unicode值是以ISO-8859-1編碼轉換而來,

23、調用name.getBytes(ISO-8859-1),將得到原始的GBK編碼值,接著,對new String()的調用將以GBK 字符集重新構造字符串的Unicode編碼。為了方便從ISO-8859-1編碼到GBK的轉換,我們可以編寫一個工具方法,如下:public String toGBK(String str)throws java.io.UnsupportedEncodingException(return new String(str.getBytes(ISO-8859-1),GBK);在數據庫中存儲和讀取中文數據對于大多數數據庫的JDBC驅動程序,在Java程序和數據庫之間傳遞數據都是以ISO-8859-1為默認 編碼格式,所以,我們在程序中向數據庫存儲包含中文的數據時,JDBC驅動程序首先把程序內部的Unicode 編碼格式的數據轉化

溫馨提示

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

最新文檔

評論

0/150

提交評論