




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、JAVA中文字符編碼問題詳解 控制臺輸出JAVA的中文字符亂碼問題一直很讓人頭疼。特別是在WEB應(yīng)用中。網(wǎng)上的分析文章和解決方案都很多,但總是針對某些特定情況的。很多次遇到亂碼問題后, 經(jīng)過極為辛苦的調(diào)試和搜索資料后終于解決,滿以為自己已經(jīng)掌握了對付這些字符亂碼怪獸的訣竅??僧?dāng)過段時間,換了個應(yīng)用或換了個環(huán)境,又會碰到那討厭的火 星文,并再次無所適從。于是下決心好好整理一下中文字符編碼問題,以方便自己記憶,也為其他程序員兄弟們提供一份參考。首先要了解JAVA處理字符的塬理。JAVA使用UNICODE來存儲字符數(shù)據(jù),處理字符時通常有叁個步驟:- 按指定的字符編碼形式,從源輸入流中讀取字符數(shù)據(jù)-
2、以UNICODE編碼形式將字符數(shù)據(jù)存儲在內(nèi)存中- 按指定的字符編碼形式,將字符數(shù)據(jù)編碼并寫入目的輸出流中。所以JAVA處理字符時總是經(jīng)過了兩次編碼轉(zhuǎn)換,一次是從指定編碼轉(zhuǎn)換為UNICODE編碼,一次是從UNICODE編碼轉(zhuǎn)換為指定編碼。如果在讀入時用 錯誤的形式解碼字符,則內(nèi)存存儲的是錯誤的UNICODE字符。而從最初文件中讀出的字符數(shù)據(jù),到最終在屏幕終端顯示這些字符,期間經(jīng)過了應(yīng)用程序的多次 轉(zhuǎn)換。如果中間某次字符處理,用錯誤的編碼方式解碼了從輸入流讀取的字符數(shù)據(jù),或用錯誤的編碼方式將字符寫入輸出流,則下一個字符數(shù)據(jù)的接收者就會編解碼 出錯,從而導(dǎo)致最終顯示亂碼。這一點,是我們分析字符編碼問
3、題以及解決問題的指導(dǎo)思想。好,現(xiàn)在我們開始一只只的解決這些亂碼怪獸。一、在JAVA文件中硬編碼中文字符,在eclipse中運行,控制臺輸出了亂碼。例如,我們在JAVA文件中寫入以下代碼:String text = “大家好”;System.out.println(text);如果我們是在eclipse里編譯運行,可能看到的結(jié)果是類似這樣的亂碼:?。那么,這是為什么呢?我們先來看看整個字符的轉(zhuǎn)換過程。1. 在eclipse窗口中輸入中文字符,并保存成UTF-8的JAVA文件。這里發(fā)生了多次字符編碼轉(zhuǎn)換。不過因為我們相信eclipse的正確性,所以我們不用分析其中的過程,只需要相信保存下的JAVA
4、文件確實是UTF-8格式。2. 在eclipse中編譯運行此JAVA文件。這里有必要詳細分析一下編譯和運行時的字符編碼轉(zhuǎn)換。- 編譯:我們用javac編譯JAVA文件時,javac不會智能到猜出你所要編譯的文件是什么編碼類型的,所以它需要指定讀取文件所用的編碼類型。默認 javac使用平臺缺省的字符編碼類型來解析JAVA文件。平臺缺省編碼是操作系統(tǒng)決定的,我們使用的是中文操作系統(tǒng),語言區(qū)域設(shè)置通常都是中國大陸,所 以平臺缺省編碼類型通常是GBK。這個編碼類型我們可以在JAVA中使用System.getProperty(“file.encoding”)來查 看。所以javac會默認使用GBK來解
5、析JAVA文件。如果我們要改變javac所用的編碼類型,就要加上-encoding參數(shù),如javac -encoding utf-8 Test.java。這里要另外提一下的是eclipse使用的是內(nèi)置的編譯器,并不能添加參數(shù),如果要為javac添加參數(shù)則建議使用ANT來編譯。不過這并非出現(xiàn)亂碼的塬因,因為eclipse可以為每個JAVA文件設(shè)置字符編碼類型,而內(nèi)置編譯器會根據(jù)此設(shè)置來編譯JAVA文件。- 運行:編譯后字符數(shù)據(jù)會以UNICODE格式存入字節(jié)碼文件中。然后eclipse會調(diào)用java命令來運行此字節(jié)碼文件。因為字節(jié)碼中的字符總是 UNICODE格式,所以java讀取字節(jié)碼文件并沒有
6、編碼轉(zhuǎn)換過程。虛擬機讀取文件后,字符數(shù)據(jù)便以UNICODE格式存儲在內(nèi)存中了。3. 調(diào)用System.out.println來輸出字符。這里又發(fā)生了字符編碼轉(zhuǎn)換。System.out.println使用了PrintStream類來輸出字符數(shù)據(jù)至控制臺。PrintStream會使用平臺缺省的編碼方式來輸出字 符。我們的中文系統(tǒng)上缺省方式為GBK,所以內(nèi)存中的UNICODE字符被轉(zhuǎn)碼成了GBK格式,并送到了操作系統(tǒng)的輸出服務(wù)中。因為我們操作系統(tǒng)是中文系 統(tǒng),所以往終端顯示設(shè)備上打印字符時使用的也是GBK編碼。如果到這一步,我們的字符其實不再是GBK編碼的話,終端就會顯示出亂碼。那么,在eclips
7、e運行帶中文字符的JAVA文件,控制臺顯示了亂碼,是在哪一步轉(zhuǎn)換錯誤呢?我們一步步來分析。- 保存JAVA文件成UTF-8后,如果再次打開你沒有看到亂碼,說明這步是正確的。- 用eclipse本身來編譯運行JAVA文件,應(yīng)該沒有問題。- System.out.println會把內(nèi)存中正確的UNICODE字符編碼成GBK,然后發(fā)到eclipse的控制臺去。等等,我們看到在Run Configuration對話框的Common標簽里,控制臺的字符編碼被設(shè)置成了UTF-8!問題就在這里。System.out.println已 經(jīng)把字符編碼成了GBK,而控制臺仍然以UTF-8的格式讀取字符,自然會出現(xiàn)
8、亂碼。將控制臺的字符編碼設(shè)置為GBK,亂碼問題解決。(這里補充一點:eclipse的控制臺編碼是繼承了workspace的設(shè)置的,通常控制臺編碼里沒有GBK的選項而且不能輸入。我們可以先在 workspace的編碼設(shè)置中輸入GBK,然后在控制臺的設(shè)置中就可以看到GBK的選項了,設(shè)置好后再把workspace的字符編碼設(shè)置改回utf- 8就是。)二、JSP文件中硬編碼中文字符,在瀏覽器上顯示亂碼。我們用eclipse編寫一個JSP頁面,使用tomcat瀏覽這個頁面時,整個頁面的中文字符都是亂碼。這是什么塬因呢?JSP頁面從編寫到在瀏覽器上瀏覽,總共有四次字符編解碼。1. 以某種字符編碼保存JSP
9、文件2. Tomcat以指定編碼來讀取JSP文件并編譯3. Tomcat向瀏覽器以指定編碼來發(fā)送HTML內(nèi)容4. 瀏覽器以指定編碼解析HTML內(nèi)容這里的四次字符編解碼,有一次發(fā)生錯誤最終顯示的就會是亂碼。我們依次來分析各次的字符編碼是如何設(shè)置的。- 保存JSP文件,這是在編輯器中設(shè)置的,比如eclipse中,設(shè)置文件字符類型為utf-8。- JSP文件開頭的% page language=“java” contentType=“text/html; charset=utf-8” pageEncoding=“utf-8”%,其中pageEncoding用來告訴tomcat此文件所用的字符編碼。這
10、個編碼應(yīng)該與eclipse 保存文件用的編碼一致。Tomcat以此編碼方式來讀取JSP文件并編譯。- page標簽中的contentType用來設(shè)置tomcat往瀏覽器發(fā)送HTML內(nèi)容所使用的編碼。這個編碼會在HTTP響應(yīng)頭中指定以通知瀏覽器。- 瀏覽器根據(jù)HTTP響應(yīng)頭中指定的字符編碼來解析HTML內(nèi)容。如:HTTP/1.1 200 OKDate: Mon, 01 Sep 2008 23:13:31 GMTServer: Apache/2.2.4 (Win32) mod_jk/1.2.26Vary: Host,Accept-EncodingSet-Cookie: JAVA2000_STYLE
11、_ID=1; Domain=; Expires=Thu, 03-Nov-2011 09:00:10 GMT; Path=/Content-Encoding: gzipTransfer-Encoding: chunkedContent-Type: text/html;charset=UTF-8另外,HTML中有個標簽meta http-equiv=“Content-Type” content=“text/html; charset=UTF-8”中也指定了charset。不過這個字符編碼只有在當(dāng)網(wǎng)頁保存在本地作為靜態(tài)網(wǎng)頁時有效,因為沒有HTTP頭,所以瀏覽器 根據(jù)此標簽來識別HTML內(nèi)容的編碼方式
12、?,F(xiàn)在在JSP文件中硬編碼出現(xiàn)亂碼的機會比較小了,因為大家都用了如eclipse的編輯器,基本上可以自動保證這幾個編碼設(shè)置的正確性?,F(xiàn)在更多碰到的是在JSP文件中從其他數(shù)據(jù)源中讀取中文字符所產(chǎn)生的亂碼問題。叁、在JSP文件中讀取字符文件并在頁面中顯示,中文字符顯示為亂碼。比如,我們在JSP文件中使用以下代碼:%BufferedReader reader = new BufferedReader(new FileReader(“D:test.txt”);String content = reader.readLine();reader.close();%=content%test.txt里保存的
13、是中文字符,但在瀏覽器上看到的亂碼。這是個經(jīng)常見到的問題。我們繼續(xù)用之前的方法一步步來分析輸入和輸出流1. test.txt是以某種編碼方式保存中文字符,比如UTF-8。2. BufferedReader直接讀取test.txt的字節(jié)內(nèi)容并以默認方式構(gòu)造字符串。分析BufferedReader的代碼,我們可以看到 BufferedReader調(diào)用了FileReader的read方法,而FileReader又調(diào)用了FileInputStream的native 的read方法。所謂native的方法,就是操作系統(tǒng)底層方法。那么我們操作系統(tǒng)是中文系統(tǒng),所以FileInputStream默認用GBK方
14、式讀取 文件。因為我們保存test.txt用的是UTF-8,所以在這里讀取文件內(nèi)容使用GBK是錯誤的編碼。3. %=content%其實就是out.print(content),這里又用到了HTTP的輸出流JspWriter,于是字符串content又被以JSP的page標簽中指定的UTF-8方式編碼成字節(jié)數(shù)組被發(fā)送到瀏覽器端。4. 瀏覽器以HTTP頭中指定的方式解碼字符,這時無論是用GBK還是UTF-8解碼,顯示的都是亂碼。可見,我們字符編碼轉(zhuǎn)換在第二步時出錯了,UTF-8的字符串被當(dāng)做GBK讀入了內(nèi)存中。解決這個亂碼問題有兩種方法,一是把test.txt用GBK保存,則FileInputS
15、tream能正確讀入中文字符;二是使用InputStreamReader來轉(zhuǎn)換字符編碼,如:InputStreamReader sr = new InputStreamReader(new FileInputStream(“D:test.txt”),“utf-8”);BufferedReader reader = new BufferedReader(sr);這樣,JAVA就會用utf-8的方式來從文件中讀取字符數(shù)據(jù)。另外,我們可以通過在java命令后帶上Dfile.encoding參數(shù)來指定虛擬機讀取文件使用的默認字符編碼,例如java -Dfile.encoding=utf-8 Test,
16、這樣,我們在JAVA代碼里用System.getProperty(“file.encoding”)取到的值為utf-8。四、JSP讀取request.getParameter里的中文參數(shù)后,在頁面顯示為亂碼。在JAVA的WEB應(yīng)用中,對request對象里的parameters的中文處理一直是常見也最難搞的一只大怪獸。經(jīng)常是剛搞定了這邊,那邊又出了亂 碼。而導(dǎo)致這種復(fù)雜性的,主要是此過程中字符編解碼次數(shù)非常多,而且無論是瀏覽器還是WEB服務(wù)器特別是TOMCAT總是不能給我們一個比較滿意的支持。首先我們來分析用GET方式上傳參數(shù)的亂碼情況。例如我們在瀏覽器地址欄輸入以下URL:http:/loc
17、alhost:8080/test/test.jsp?param=大家好我們的JSP代碼如此處理param這個參數(shù):% String text = request.getParameter(“param”); %=text%而就這么簡單的兩句代碼,我們很有可能在頁面上看到這樣的亂碼:?ó?網(wǎng)上對處理request.getParamter中的亂碼有很多文章和方法,也都是正確的,只是方法太多讓人一直不明白到底是為什么。這里給大家分析一下到底是怎么一回事。首先,我們來看看與request對象有哪些相關(guān)的編碼設(shè)置:1. JSP文件的字符編碼2. 請求這個帶參數(shù)URL的源頁面的字符編碼3. IE
18、的高級設(shè)置中的選項“總以utf-8方式發(fā)送URL地址”4. TOMCAT的server.xml中配置URIEncoding5. 函數(shù)request.setCharacterEncoding()6. JS的encodeURIComponent函數(shù)與JAVA的URLDecoder類這么多條相關(guān)編碼設(shè)置,也難怪大家被搞得頭暈了。這里給大家根據(jù)各種情況給大家一一分析一下。見下表: 以上表格里的現(xiàn)象,除了指名在IE7上,其他全是在IE6上測試的結(jié)果。由這個表我們可以看到,IE的“總以utf-8方式發(fā)送URL地址”設(shè)置并不影響對parameter的解析,而從頁面請求URL和從地址欄輸入URL居然也有不同的
19、表現(xiàn)。根據(jù)這個表列出的現(xiàn)象,大家只要用smartSniff抓幾個網(wǎng)絡(luò)包,并稍稍調(diào)查一下TOMCAT的源代碼,就可以得出以下結(jié)論:1. IE設(shè)置中的“總以utf-8方式發(fā)送URL地址”只對URL的PATH部分起作用,對查詢字符串是不起作用的。也就是說,如果勾選了這個選項,那么類似 http:/localhost:8080/test/大家好.jsp?param=大家好這種URL,前一個“大家好”將被轉(zhuǎn)化成utf-8形式,而 后一個并沒有變化。這里所說的utf-8形式,其實應(yīng)該叫utf-8+escape形式,即%B4%F3%BC%D2%BA%C3這種形式。那么,查詢字符串中的中文字符,到底是用什么編
20、碼傳送到服務(wù)器的呢?答案是系統(tǒng)默認編碼,即GBK。也就是說,在我們中文操作系統(tǒng)上,傳送給WEB服務(wù)器的查詢字符串,總是以GBK來編碼的。2. 在頁面中通過鏈接或location重定向或open新窗口的方式來請求一個URL,這個URL里面的中文字符是用什么編碼的?答:是用該頁面的編碼類 型。也就是說,如果我們從某個源JSP頁面上的鏈接來訪問http:/localhost:8080/test/test.jsp?param=大家好 這個URL,如果源JSP頁面的編碼是UTF-8,則大家好這幾個字的編碼就是UTF-8。而在地址欄上直接輸入URL地址,或者從系統(tǒng)剪貼板粘貼到地址欄上,這個輸入并非從頁面中
21、發(fā)起的,而是由操作系統(tǒng)發(fā)起的,所以這個編碼只可能是系統(tǒng)的默認 編碼,與任何頁面無關(guān)。我們還發(fā)現(xiàn),在不同的瀏覽器上,用鏈接方式打開的頁面,如果在地址欄上再敲個回車,顯示的結(jié)果也會不同。IE上敲回車后顯示不變 化,而傲游上可能就會有亂碼或亂碼消失的變化。說明IE上敲回車,實際發(fā)送的是之前記憶下來的內(nèi)存中的URL,而傲游上發(fā)送的從當(dāng)前地址欄重新獲取的 URL。3. TOMCAT的URIEncoding如果不加以設(shè)置,則默認使用ISO-8859-1來解碼URL,設(shè)置后便用設(shè)置了的編碼方式來解碼。這個解碼同時包 括PATH部分和查詢字符串部分??梢?,這個參數(shù)是對用GET方式傳遞的中文參數(shù)最關(guān)鍵的設(shè)置。不
22、過,這個參數(shù)只對GET方式傳遞的參數(shù)有效,對POST 的無效。分析TOMCAT的源代碼我們可以看到,在請求一個頁面時,TOMCAT會嘗試構(gòu)造一個Request對象,在這個對象里,會從 Server.xml里讀取URIEncoding的值,并賦值給Parameters類的queryStringEncoding變量,而這個變量將在 解析request.getParameter中的GET參數(shù)時用來指導(dǎo)字符解碼。4. request.setCharacterEncoding函數(shù)只對POST的參數(shù)有效,對GET的參數(shù)無效。且這個函數(shù)必須是在第一次調(diào)用 request.getParameter之前使用。這
23、是因為Parameters類有兩個字符編碼參數(shù),一個是encoding,另一個是 queryStringEncoding,而setCharacterEncoding設(shè)置的是encoding,這個是在解析POST的參數(shù)是才用到 的。所以,這就導(dǎo)致了我們通常都要分開處理POST和GET的字符編碼,用TOMCAT自帶的filter只能處理POST的,另外要設(shè)置URIEncoding來設(shè)置GET的。這樣很麻煩而且URIEncoding無法根據(jù)內(nèi)容來動態(tài)區(qū)分編碼,總還是一個問題。在調(diào)查TOMCAT的代碼時發(fā)現(xiàn)了另一個在server.xml里的參數(shù)useBodyEncodingForURI,可以解決這個問
24、題。這個參數(shù)設(shè)成 true后,TOMCAT就會用request.setCharacterEncoding所設(shè)置的字符編碼來同樣解析GET參數(shù)了。這樣,那個 SetCharacterEncodingFilter就可以同時處理GET和POST參數(shù)了。知道了以上知識后,我們再來分析一下前面表格中列出的幾個典型現(xiàn)象。第一條,請求源頁面的編碼為UTF-8,而TOMCAT的URIEncoding未指定,則TOMCAT用ISO8859-1方式來解碼參數(shù),所以從request中讀出來后,內(nèi)存中存儲的為錯誤的UNICODE數(shù)據(jù),導(dǎo)致之后到屏幕顯示的所有轉(zhuǎn)換全部出錯。第九條,請求源頁面編碼為GBK,而TOMCAT
25、的URIEncoding也為GBK,TOMCAT用GBK方式去解碼塬本用GBK編碼的字符,解碼正確,內(nèi)存中的UNICODE值正確,最終顯示正確的中文。第十叁條,請求源頁面編碼為UTF-8,TOMCAT的URIEncoding也為UTF-8,而在IE6中最終顯示的中文字符,如果是奇數(shù)個數(shù),則最后一個會顯示為亂碼。這是為什么呢?我的猜測是,這是因為IE6將URL地址發(fā)送時,對查詢字符串是直接對UTF-8格式的字符使用GBK來編碼,而不是對UNICODE的字符來用GBK編 碼,所以UTF-8的數(shù)據(jù)沒有經(jīng)過UNICODE而直接編碼成了GBK。而到了TOMCAT這邊,GBK的編碼又被當(dāng)成UTF-8做了解碼。所以這個過程 中經(jīng)過了UTF-8轉(zhuǎn)換成GBK,然后又從GBK轉(zhuǎn)換成UTF-8的過程,而這種轉(zhuǎn)換,恰好就會出現(xiàn)奇數(shù)個中文字符串的最后一位為亂碼的現(xiàn)象。而在IE7 中,估計把這種現(xiàn)象當(dāng)做BUG已經(jīng)被解決了,即在發(fā)送地址時會先轉(zhuǎn)成UNICODE再編碼成GBK。那么估計在IE7的瀏覽器+中文操作系統(tǒng)環(huán)境下,如果 我們把TOMCAT的URIEncoding設(shè)置成GBK,無論JSP編碼成什么格式,都
溫馨提示
- 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 智慧課堂省級課題申報書
- 數(shù)字孿生課題申報書
- 課題立項申報書幼兒園
- 孔子學(xué)堂課題申報書
- 兵團課題申報書
- 經(jīng)濟類課題申報書范例
- 城市更新課題申報書范本
- 醫(yī)院消防勞務(wù)合同范本
- 課題申報書是啥
- 教育科研方法課題申報書
- 高中英語丨高考核心高頻詞匯
- 《營養(yǎng)均衡膳食指南》課件
- 《數(shù)智化技術(shù)應(yīng)用與創(chuàng)新》課件 第1章 走進數(shù)智化時代
- 《智能家居系統(tǒng)》課件
- 基礎(chǔ)模塊下冊《中國人民站起來了》2
- 繼電保護業(yè)務(wù)技能實操題庫
- 員工請假管理制度-員工請假管理制度范文
- 畢業(yè)設(shè)計(論文)鋼包用耐火材料的設(shè)計及優(yōu)化
- 關(guān)于材料認質(zhì)認價的申請(材料價格調(diào)差報告)
- 第2章 土中應(yīng)力
- (完整版)冠詞練習(xí)題及答案解析
評論
0/150
提交評論