




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、Java中文亂碼問題產(chǎn)生原因分析在計(jì)算機(jī)中,只有二進(jìn)制的數(shù)據(jù),不管數(shù)據(jù)是在內(nèi)存中,還是在外部存儲設(shè)備上。對于我們所看到的 字符,也是以二進(jìn)制數(shù)據(jù)的形式存在的。不同字符對應(yīng)二進(jìn)制數(shù)的規(guī)則,就是字符的編碼。字符編碼的集 合稱為字符集。17.1.1常用字符集在早期的計(jì)算機(jī)系統(tǒng)中,使用的字符非常少,這些字符包括26個英文字母、數(shù)字符號和一些常用符 號(包括控制符號),對這些字符進(jìn)行編碼,用1個字節(jié)就足夠了(1個字節(jié)可以表示28=256種字符)。然 而實(shí)際上,表示這些字符,只使用了 1個字節(jié)的7位,這就是ASCII編碼。ASCIIASCII (American Standard Code for Inf
2、ormation Interchange,美國信息互換標(biāo)準(zhǔn)代碼),是基于常用的 英文字符的一套電腦編碼系統(tǒng)。每一個ASCII碼與一個8位(bit)二進(jìn)制數(shù)對應(yīng)。其最高位是0,相應(yīng)的 十進(jìn)制數(shù)是0127。例如,數(shù)字字符“0”的編碼用十進(jìn)制數(shù)表示就是48。另有128個擴(kuò)展的ASCII碼, 最高位都是1,由一些圖形和畫線符號組成。ASCII是現(xiàn)今最通用的單字節(jié)編碼系統(tǒng)。ASCII用一個字節(jié)來表示字符,最多能夠表示256種字符。隨著計(jì)算機(jī)的普及,許多國家都將本地的 語言符號引入到計(jì)算機(jī)中,擴(kuò)展了計(jì)算機(jī)中字符的范圍,于是就出現(xiàn)了各種不同的字符集。ISO8859-1因?yàn)锳SCII碼中缺少、u和許多書寫其他
3、語言所需的字符,為此,可以通過指定128以后的字符來 擴(kuò)展ASCII碼。國際標(biāo)準(zhǔn)組織(ISO)定義了幾個不同的字符集,它們是在ASCII碼基礎(chǔ)上增加了其他語 言和地區(qū)需要的字符。其中最常用的是ISO8859-1,通常叫做Latin-1。Latin-1包括了書寫所有西方歐洲語 言不可缺少的附加字符,其中0127的字符與ASCII碼相同。ISO 8859另外定義了 14個適用于不同文字 的字符集(8859-2到8859-15)。這些字符集共享0127的ASCII碼,只是每個字符集都包含了 128255 的其他字符。GB2312 和 GBKGB2312是中華人民共和國國家標(biāo)準(zhǔn)漢字信息交換用編碼,全稱
4、信息交換用漢字編碼字符集一基本 集,標(biāo)準(zhǔn)號為GB2312-80,是一個由中華人民共和國國家標(biāo)準(zhǔn)總局發(fā)布的關(guān)于簡化漢字的編碼,通行于 中國大陸和新加坡,簡稱國標(biāo)碼。因?yàn)橹形淖址麛?shù)量較多,所以采用兩個字節(jié)來表示一個字符,分別稱為高位和低位。為了和ASCII碼 有所區(qū)別,中文字符的每一個字節(jié)的最高位都用1來表示。GB2312字符集是幾乎所有的中文系統(tǒng)和國際 化的軟件都支持的中文字符集,也是最基本的中文字符集。它包含了大部分常用的一、二級漢字和9區(qū)的 符號,其編碼范圍是高位0 xa1-0 xfe,低位也是0 xa1-0 xfe,漢字從0 xb0a1開始,結(jié)束于0 xf 7fe。為了對更多的字符和符號進(jìn)
5、行編碼,由前電子部科技質(zhì)量司和國家技術(shù)監(jiān)督局標(biāo)準(zhǔn)化司于1995年12 月頒布了 GBK(K是“擴(kuò)展”的漢語拼音第一個字母)編碼規(guī)范,在新的編碼系統(tǒng)里,除了完全兼容GB2312 外,還對繁體中文、一些不常用的漢字和許多符號進(jìn)行了編碼。它也是現(xiàn)階段Windows和其他一些中文操 作系統(tǒng)的默認(rèn)字符集,但并不是所有的國際化軟件都支持該字符集。不過要注意的是GBK不是國家標(biāo)準(zhǔn), 它只是規(guī)范。GBK字符集包含了 20 902個漢字,其編碼范圍是0 x8140-0 xfefe。每個國家(或區(qū)域)都規(guī)定了計(jì)算機(jī)信息交換用的字符編碼集,這就造成了交流上的困難。想像一下, 你發(fā)送一封中文郵件給一位遠(yuǎn)在西班牙的朋友
6、,當(dāng)郵件通過網(wǎng)絡(luò)發(fā)送出去的時候,你所書寫的中文字符會 按照本地的字符集GBK轉(zhuǎn)換為二進(jìn)制編碼數(shù)據(jù),然后發(fā)送出去。當(dāng)你的朋友接收到郵件(二進(jìn)制數(shù)據(jù)) 后,查看信件時,會按照他所用系統(tǒng)的字符集,將二進(jìn)制編碼數(shù)據(jù)解碼為字符,然而由于兩種字符集之間 編碼的規(guī)則不同,導(dǎo)致轉(zhuǎn)換出現(xiàn)亂碼。這是因?yàn)椋诓煌淖址g,同樣的數(shù)字可能對應(yīng)了不同的符 號,也可能在另一種字符集中,該數(shù)字沒有對應(yīng)符號。為了解決上述問題,統(tǒng)一全世界的字符編碼,由Unicode協(xié)會1制定并發(fā)布了 Unicode編碼。UnicodeUnicode (統(tǒng)一的字符編碼標(biāo)準(zhǔn)集)使用065535的雙字節(jié)無符號數(shù)對每一個字符進(jìn)行編碼。它不僅 包含
7、來自英語和其他西歐國家字母表中的常見字母和符號,也包含來自古斯拉夫語、希臘語、希伯來語、 阿拉伯語和梵語的字母表。另外還包含漢語和日語的象形漢字和韓國的Hangul音節(jié)表。目前已經(jīng)定義了 40000多個不同的Unicode字符,剩余25000個空缺留給將來擴(kuò)展使用。其中大約201 Unicode協(xié)會是由IBM、微軟、Adobe、SUN、加州大學(xué)伯克利分校等公司和組織所組成的非營利性組織。 000個字符用于漢字,另外11000左右的字符用于韓語音節(jié)。Unicode中0255的字符與ISO8859-1中的 一致。Unicode編碼對于英文字符采取前面加“0”字節(jié)的策略實(shí)現(xiàn)等長兼容。如“a”的ASC
8、II碼為0 x61, Unicode 碼就為 0 x00, 0 x61。UTF-8使用Unicode編碼,一個英文字符要占用兩個字節(jié),在Internet上,大多數(shù)的信息都是用英文來表示 的,如果都采用Unicode編碼,將會使數(shù)據(jù)量增加一倍。為了減少存儲和傳輸英文字符數(shù)據(jù)的數(shù)據(jù)量,可 以使用UTF-8編碼。UTF-8 全稱是 Eight-bit UCS Transformation Format (UCS, Universal Character Set,通用字符集,UCS 是所有其他字符集標(biāo)準(zhǔn)的一個超集)。對于常用的字符,即0127的ASCII字符,UTF-8用一個字節(jié)來表 示,這意味著只包
9、含7位ASCII字符的字符數(shù)據(jù)在ASCII和UTF-8兩種編碼方式下是一樣的。如果字符對 應(yīng)的Unicode碼是0 x0000,或在0 x0080與0 x007f之間,對應(yīng)的UTF-8編碼是兩個字節(jié),如果字符對應(yīng)的 Unicode碼在0 x0800與0 xffff之間,對應(yīng)的UTF-8編碼是三個字節(jié)。因?yàn)橹形淖址腢nicode編碼在0 x0800 與0 xffff之間,所以數(shù)據(jù)如果是中文,采用UTF-8編碼數(shù)據(jù)量會增加50%。Unicode與UTF-8轉(zhuǎn)換的規(guī)則簡述如下:如果Unicode編碼的16位二進(jìn)制數(shù)的前9位是0,則UTF-8編碼用1個字節(jié)來表示,這個字節(jié)的 首位是“0”,剩下的7位
10、與原二進(jìn)制數(shù)據(jù)的后7位相同。例如:Unicode 編碼:u0061 = 00000000 01100001UTF-8 編碼:01100001 = 0 x61如果Unicode編碼的16位二進(jìn)制數(shù)的頭5位是0,則UTF-8編碼用2個字節(jié)來表示,首字節(jié)以“110” 開頭,后面的5位與原二進(jìn)制數(shù)據(jù)除去前5個零后的最高5位相同;第二個字節(jié)以“ 10”開頭,后面的6 位與原二進(jìn)制數(shù)據(jù)中的低6位相同。例如:Unicode 編碼:u00A9 = 00000000 10101001UTF-8 編碼:11000010 10101001 = 0 xC2 0 xA9如果不符合上述兩個規(guī)則,則用三個字節(jié)表示。第一個字
11、節(jié)以“ 1110”開頭,后四位為原二進(jìn)制 數(shù)據(jù)的高四位;第二個字節(jié)以“10”開頭,后六位為原二進(jìn)制數(shù)據(jù)中間的六位;第三個字節(jié)以10”開頭, 后六位為原二進(jìn)制數(shù)據(jù)的低六位。例如:Unicode 編碼:u4E2D = 01001110 00101101UTF-8 編碼:11100100 10111000 10101101 = 0 xE4 0 xB8 0 xADI! 注意在UTF-8編碼的多字節(jié)串中,第一個字節(jié)開頭1”的數(shù)目就是整個字符串中字節(jié)的數(shù)目。17.1.2對亂碼產(chǎn)生過程的分析為了讓使用Java語言編寫的程序能在各種語言的平臺下運(yùn)行,Java在其內(nèi)部使用Unicode字符集來表 示字符,這樣就
12、存在Unicode字符集和本地字符集進(jìn)行轉(zhuǎn)換的過程。當(dāng)在Java中讀取字符數(shù)據(jù)的時候,需 要將本地字符集編碼的數(shù)據(jù)轉(zhuǎn)換為Unicode編碼,而在輸出字符數(shù)據(jù)的時候,則需要將Jnicode編碼轉(zhuǎn)換為 本地字符集編碼。例如,在中文系統(tǒng)下,從控制臺讀取一個字符“中”,實(shí)際上讀取的是“中”的GBK編碼0 xD6D0, 在Java語言中要將GBK編碼轉(zhuǎn)換為Unicode編碼0 x4E2D,此時,在內(nèi)存中,字符“中”對應(yīng)的數(shù)值就是 0 x4E2D,當(dāng)我們向控制臺輸出字符時,Java語言將Unicode編碼再轉(zhuǎn)換為GBK編碼,輸出到控制臺,中 文系統(tǒng)再根據(jù)GBK字符集畫出相應(yīng)的字符。從上述過程來看,讀取和寫
13、入的過程是可逆的,那么理應(yīng)不會出現(xiàn)中文亂碼問題。然而,實(shí)際應(yīng)用的 情形,比上述過程要復(fù)雜得多。在Web應(yīng)用中,通常都包括了瀏覽器、Web服務(wù)器、Web應(yīng)用程序和數(shù) 據(jù)庫等部分,每一部分都有可能使用不同的字符集,從而導(dǎo)致字符數(shù)據(jù)在各種不同的字符集之間轉(zhuǎn)換時, 出現(xiàn)亂碼的問題。在Java語言中,不同字符集編碼的轉(zhuǎn)換,都是通過Unicode編碼作為中介來完成的。例如,GBK編 碼的字符“中”要轉(zhuǎn)換為ISO-8859-1 (同ISO8859-1)編碼,其過程如下:因?yàn)樵贘ava中的字符,都是用Unicode來表示的,所以GBK編碼的字符“中”要轉(zhuǎn)換為Unicode 表示:0 xD6D0-0 x4E2D
14、。將字符“中”的Unicode編碼轉(zhuǎn)換為ISO-8859-1編碼,因?yàn)閁nicode編碼0 x4E2D在ISO-8859-1 中沒有對應(yīng)的編碼,于是得到0 x3f,也就是字符“?”。下面的代碼演示了這一過程:/GBK編碼的字符“中”轉(zhuǎn)換為Unicode編碼表示String str=中;/將字符“中”的Unicode編碼轉(zhuǎn)換為ISO-8859-1編碼byte b=str.getBytes(ISO-8859-1);for(int i=0;ib.length;i+)(/輸出轉(zhuǎn)換后的二進(jìn)制代碼。System.out.print(bi);當(dāng)從Unicode編碼向某個字符集轉(zhuǎn)換時,如果在該字符集中沒有對應(yīng)
15、的編碼,則得到0 x3f (即問號字 符?)。這就是為什么有時候我們輸入的是中文,在輸出時卻變成了問號。從其他字符集向Unicode編碼轉(zhuǎn)換時,如果這個二進(jìn)制數(shù)在該字符集中沒有標(biāo)識任何的字符,則得到 的結(jié)果是0 xfffdo例如一個GBK的編碼值0 x8140,從GB2312向Unicode轉(zhuǎn)換,然而由于0 x8140不在 GB2312字符集的編碼范圍(0 xa1a1-0 xfefe),當(dāng)然也就沒有對應(yīng)任何的字符,所以轉(zhuǎn)換后會得到0 xfffd。 下面的代碼演示了這一過程。/構(gòu)造一個二進(jìn)制數(shù)據(jù)。byte buf=(byte)0 x81,(byte)0 x40,(byte)0 xb0,(byte
16、)0 xa1;/將二進(jìn)制數(shù)據(jù)按照GB2312向Unicode編碼轉(zhuǎn)換。String str=new String(buf,GB2312);for(int i=0;istr.length();i+)/取出字符串中的每個Unicode編碼的字符。char ch=str.charAt(i);/務(wù)該字符對應(yīng)的Unicode編碼以十六進(jìn)制的形式輸出。System.out.print(Integer.toHexString(int)ch);System.out.print(-);/輸出該字符。System.out.println(ch);在輸出字符和字符串的時候,會從Unicode編碼向中文系統(tǒng)默認(rèn)的編碼
17、GBK轉(zhuǎn)換,由于Unicode編碼 0 xfffd在GBK字符集中沒有對應(yīng)的編碼,于是得到0 x3f,輸出字符“?”。最后輸出的結(jié)果如下:fffd-?40-554a-啊從上述所知,由于存在著多種不同的字符集,在各種字符集之間進(jìn)行轉(zhuǎn)換,就有可能出現(xiàn)亂碼,同樣 是中文字符集GB2312和GBK,由于編碼范圍的不同,某些字符在轉(zhuǎn)換時也會出現(xiàn)亂碼。在一個使用了數(shù)據(jù)庫的Web應(yīng)用程序中,亂碼可能會在多個環(huán)節(jié)產(chǎn)生。由于瀏覽器會根據(jù)本地系統(tǒng)默 認(rèn)的字符集來提交數(shù)據(jù),而Web容器默認(rèn)采用的是ISO-8859-1的編碼方式解析POST數(shù)據(jù),在瀏覽器提 交中文數(shù)據(jù)后,Web容器會按照ISO-8859-1字符集來解
18、碼數(shù)據(jù),在這一環(huán)節(jié)可能會導(dǎo)致亂碼的產(chǎn)生。由于 大多數(shù)數(shù)據(jù)庫的JDBC驅(qū)動程序默認(rèn)采用ISO-8859-1的編碼方式在Java程序和數(shù)據(jù)庫之間傳遞數(shù)據(jù),我 們的程序在向數(shù)據(jù)庫中存儲包含中文的數(shù)據(jù)時,JDBC驅(qū)動首先將程序內(nèi)部的Unicode編碼格式的數(shù)據(jù)轉(zhuǎn) 化為ISO-8859-1的格式,然后傳遞到數(shù)據(jù)庫中,在這一環(huán)節(jié)可能會導(dǎo)致亂碼的產(chǎn)生。目前流行的關(guān)系型數(shù) 據(jù)庫系統(tǒng)都支持?jǐn)?shù)據(jù)庫編碼,也就是說在創(chuàng)建數(shù)據(jù)庫時可以指定它自己的字符集設(shè)置,數(shù)據(jù)庫的數(shù)據(jù)以指 定的編碼形式存儲。當(dāng)JDBC驅(qū)動向數(shù)據(jù)庫中保存數(shù)據(jù)時,有可能還會發(fā)生字符集的轉(zhuǎn)換。正是由于在 Web應(yīng)用程序運(yùn)行過程中,輸入的中文字符需要在不同的
19、字符集之間來回轉(zhuǎn)換,也就導(dǎo)致了中文亂碼問題 的頻繁出現(xiàn)。圖17-1描述了在Web應(yīng)用的請求響應(yīng)過程中,發(fā)生的字符編碼轉(zhuǎn)換過程,其中瀏覽器是正6.0,Web 容器的是 Tomcat 6.0.16。從圖17-1描述的過程中可以看到,如果在Web應(yīng)用程序中不指定任何的字符集,從瀏覽器端傳來的 中文字符,輸出回瀏覽器時,可以正常顯示(以簡體中文的方式查看網(wǎng)頁)。然而,事情并沒有這么簡單, 在Servlet/JSP中,可能存在著直接寫入的或從其他來源讀取的中文字符,如果這些字符對應(yīng)的Unicode碼 是從GB2312編碼轉(zhuǎn)換而來,那么以ISO-8859-1編碼方式輸出,這些字符將不能正常顯示。所以對于中
20、文 的處理,應(yīng)該在圖17-1和的位置明確指定使用GB2312或GBK字符集。圖17-1在Web請求響應(yīng)過程中,中文字符編碼的轉(zhuǎn)換過程中文亂碼問題的解決方案只要掌握了中文亂碼問題產(chǎn)生的原因,然后對癥下藥,就可以順利地解決這些問題。下面我們對容易 產(chǎn)生亂碼問題的場景進(jìn)行分析,并提出解決方案。以POST方法提交的表單數(shù)據(jù)中有中文字符由于Web容器默認(rèn)的編碼方式是ISO-8859-1,在Servlet/JSP程序中,通過請求對象的getParameter() 方法得到的字符串是以ISO-8859-1轉(zhuǎn)換而來,這是導(dǎo)致亂碼產(chǎn)生的原因之一。為了避免容器以ISO-8859-1 的編碼方式返回字符串,對于以
21、POST方法提交的表單數(shù)據(jù),可以在獲取請求參數(shù)值之前,調(diào)用 request.setCharacterEncoding(GBK”),明確指定請求正文使用的字符編碼方式是GBK。在向?yàn)g覽器發(fā)送 中文數(shù)據(jù)之前,調(diào)用response.setContentType(text/html;charset=GBK”),指定輸出內(nèi)容的編碼方式是GBK。對于JSP頁面,在獲取請求參數(shù)值之前,寫上下面的代碼:為了指定輸出內(nèi)容的編碼格式,設(shè)置page指令contentType屬性,如下:在Web容器轉(zhuǎn)換JSP頁面后的Servlet類中,會自動添加下面的代碼:response.setContentType(text/h
22、tml; charset=GBK);以GET方法提交的表單數(shù)據(jù)中有中文字符當(dāng)提交表單采用GET方法時,提交的數(shù)據(jù)作為查詢字符串被附加到URL的末端,發(fā)送到服務(wù)器,此 時在服務(wù)器端調(diào)用setCharacterEncoding()方法也就沒有作用了。我們需要在得到請求參數(shù)的值后,自己做 正確的編碼轉(zhuǎn)換。String name = request.getParameter(name);name=new String(name.getBytes(ISO-8859-1),GBK);在第一行,調(diào)用getParameter()方法得到的字符串name的Unicode值是以ISO-8859-1編碼轉(zhuǎn)換而來,
23、調(diào)用name.getBytes(ISO-8859-1),將得到原始的GBK編碼值,接著,對new String()的調(diào)用將以GBK 字符集重新構(gòu)造字符串的Unicode編碼。為了方便從ISO-8859-1編碼到GBK的轉(zhuǎn)換,我們可以編寫一個工具方法,如下:public String toGBK(String str)throws java.io.UnsupportedEncodingException(return new String(str.getBytes(ISO-8859-1),GBK);在數(shù)據(jù)庫中存儲和讀取中文數(shù)據(jù)對于大多數(shù)數(shù)據(jù)庫的JDBC驅(qū)動程序,在Java程序和數(shù)據(jù)庫之間傳遞數(shù)據(jù)都是以ISO-8859-1為默認(rèn) 編碼格式,所以,我們在程序中向數(shù)據(jù)庫存儲包含中文的數(shù)據(jù)時,JDBC驅(qū)動程序首先把程序內(nèi)部的Unicode 編碼格式的數(shù)據(jù)轉(zhuǎn)化
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度知識產(chǎn)權(quán)贈與及許可協(xié)議書范文
- 二零二五年度資料員招聘與知識產(chǎn)權(quán)保護(hù)與運(yùn)用協(xié)議
- 2025年度電力設(shè)備安裝與檢修服務(wù)合同
- 二零二五年度科研機(jī)構(gòu)實(shí)驗(yàn)室年租房合同
- 二零二五年度廣告公司兼職設(shè)計(jì)師合作協(xié)議
- 2025年度珠寶玉石進(jìn)出口貿(mào)易合同
- 網(wǎng)絡(luò)安全防御策略知識題庫
- 探索阿凡提的故事的寓言色彩
- 農(nóng)業(yè)環(huán)境保護(hù)工作要點(diǎn)
- 公司年度運(yùn)營計(jì)劃與目標(biāo)分解書
- 零星工程(零星用工)簽認(rèn)單
- 氬氣安全技術(shù)說明書MSDS
- 四年級數(shù)學(xué)下冊教案-練習(xí)一-北師大版
- 5G手機(jī)無線通訊濾波芯片產(chǎn)業(yè)化項(xiàng)目環(huán)境影響報(bào)告表
- 《對外援援助成套項(xiàng)目勘察設(shè)計(jì)取費(fèi)標(biāo)準(zhǔn)內(nèi)部暫行規(guī)定(稿)》
- 通用反應(yīng)單元工藝
- 空冷塔施工方案
- 電飯煲的智能控制系統(tǒng)設(shè)計(jì)
- 儲罐玻璃鋼內(nèi)防腐
- 2013-2015北京地鐵部分線路年客流量
- 機(jī)械設(shè)計(jì)說明書
評論
0/150
提交評論