JAVA中文字元編碼問題詳解 控制檯輸出
許多檔案的預設編碼是ISO-8859-1,而中文作業系統的預設編碼是GB18030,在此工作空間中建立的工程編碼是GB18030.我們常用的編碼是UTF-8,能夠使得外掛有更好的國際支援。在編寫JSP檔案時如果沒有更改預設編碼,則中文無法正常輸出,出現亂碼。Eclipse工作空間的預設編碼是作業系統預設編碼,和簡體中文作業系統(windows xp,windows 2000)編碼一致,為GB18030,則初始建立的java檔案也是GB18030。
Java檔案------編譯成位元組碼-----java執行配置-----輸出到控制檯
必須保證每個環節的內部轉化過程正確,才不會出現亂碼。
Eclipse中修改工程空間編碼格式方式
1、windows->Preferences...開啟"首選項"對話方塊,左側導航樹,導航到general->Workspace,右側Text fileencoding,選擇Other,改變為UTF-8,以後新建立工程其屬性對話方塊中的Text fileencoding即為UTF-8。
2、windows->Preferences...開啟"首選項"對話方塊,左側導航樹,導航到general->ContentTypes,右側Context Types樹,點開Text中每一顆子項,並在中輸入"UTF-8",點update!
其他java應用開發相關的檔案如:properties、XML等已經由Eclipse預設指定,分別為ISO8859-1,UTF-8,如開發中確需改變編碼格式則可以在此指定。
MyEclipse編碼設定
我的Myeclipse安裝後編碼預設是GB18030,外面的人一般推薦用UTF-8。如果在匯入專案後發現亂碼現象,那是編碼設定設定不對。
全域性編碼設定:編碼設定的方法:ToolBar-->Window-->Preferences-->General-->Workspace-->Textfile encoding,設定合適的編碼。
區域性編碼設定:在原始碼按右鍵-->General-->Editors-->TestEditors-->Spelling-->Encoding,這裡是設定單個檔案的編碼。
推薦還是使用全域性編碼設定吧。
4、經過上述三步,新建java檔案即為UTF-8編碼,Eclipse編譯、執行、除錯都沒問題,但是做RCP應用的Product輸出時、或者外掛輸出時,則總是出錯,要麼不能編譯通過(輸出時要重新compile)、要麼輸出的外掛執行時中文顯示亂碼。此時需要再RCP應用、或外掛Plugin工程的build.properties中增加一行,javacDefaultEncoding..= UTF-8。讓輸出時編譯知道java原始檔時UTF-8編碼。這個設定需要保證所有的java原始檔時UTF-8編碼格式,如果不全是,可以參考 Eclipse幫中(Plug-inDevelopment Environment Guide > Reference > Feature and Plug-in Buildconfiguration),建議全部java原始檔是UTF-8編碼。
如果外掛開發、RCP應用開發原來基於其他編碼,如GB18030,想轉換為UTF-8,則首先,做以上工作;然後通過查詢編碼轉換工具,如基於 iconv的批量轉換工具,將原編碼轉換為UTF-8編碼,注意只轉換java原始檔,其他型別檔案可能已經是比較合適的編碼了;將原工程屬性中的 Text fileencoding,從原編碼改為UTF-8即可。
System.out.println使用了PrintStream類來輸出字元資料至控制檯。PrintStream會使用平臺預設的編碼方式來輸出字 符。我們的中文系統上預設方式為GBK,所以記憶體中的UNICODE字元被轉碼成了GBK格式,並送到了作業系統的輸出服務中。因為我們作業系統是中文系 統,所以往終端顯示裝置上列印字元時使用的也是GBK編碼。如果到這一步,我們的字元其實不再是GBK編碼的話,終端就會顯示出亂碼。
- System.out.println會把記憶體中正確的UNICODE字元編碼成GBK,然後發到eclipse的控制檯去。等等,我們看到在Run Configuration對話方塊的Common標籤裡,控制檯的字元編碼被設定成了UTF-8!問題就在這裡。System.out.println已經把字元編碼成了GBK,而控制檯仍然以UTF-8的格式讀取字元,自然會出現亂碼。
將控制檯的字元編碼設定為GBK,亂碼問題解決。
(這裡補充一點:eclipse的控制檯編碼是繼承了workspace的設定的,通常控制檯編碼裡沒有GBK的選項而且不能輸入。我們可以先在 workspace的編碼設定中輸入GBK,然後在控制檯的設定中就可以看到GBK的選項了,設定好後再把workspace的字元編碼設定改回utf- 8就是。)
JSP頁面從編寫到在瀏覽器上瀏覽,總共有四次字元編解碼。
1. 以某種字元編碼儲存JSP檔案
2. Tomcat以指定編碼來讀取JSP檔案並編譯
3. Tomcat向瀏覽器以指定編碼來發送HTML內容
4. 瀏覽器以指定編碼解析HTML內容
這裡的四次字元編解碼,有一次發生錯誤最終顯示的就會是亂碼。我們依次來分析各次的字元編碼是如何設定的。
JSP檔案開頭的《%@page language=“java” contentType=“text/html; charset=utf-8”pageEncoding=“utf-8”%》,其中pageEncoding用來告訴tomcat此檔案所用的字元編碼。這個編碼應該與eclipse儲存檔案用的編碼一致。Tomcat以此編碼方式來讀取JSP檔案並編譯。
- page標籤中的contentType用來設定tomcat往瀏覽器傳送HTML內容所使用的編碼。這個編碼會在HTTP響應頭中指定以通知瀏覽器。
比如,我們在JSP檔案中使用以下程式碼:
《%
BufferedReader reader =new BufferedReader(new FileReader(“D:\\test.txt”));
String content = reader.readLine();
reader.close();
%》
《%=content%》
test.txt裡儲存的是中文字元,但在瀏覽器上看到的亂碼。這是個經常見到的問題。我們繼續用之前的方法一步步來分析輸入和輸出流
1. test.txt是以某種編碼方式儲存中文字元,比如UTF-8。
2. BufferedReader直接讀取test.txt的位元組內容並以預設方式構造字串。分析BufferedReader的程式碼,我們可以看到 BufferedReader呼叫了FileReader的read方法,而FileReader又呼叫了FileInputStream的native 的read方法。所謂native的方法,就是作業系統底層方法。那麼我們作業系統是中文系統,所以FileInputStream預設用GBK方式讀取 檔案。因為我們儲存test.txt用的是UTF-8,所以在這裡讀取檔案內容使用GBK是錯誤的編碼。
3. 《%=content%》其實就是out.print(content),這裡又用到了HTTP的輸出流JspWriter,於是字串content又被以JSP的page標籤中指定的UTF-8方式編碼成位元組陣列被髮送到瀏覽器端。
4. 瀏覽器以HTTP頭中指定的方式解碼字元,這時無論是用GBK還是UTF-8解碼,顯示的都是亂碼。
可見,我們字元編碼轉換在第二步時出錯了,UTF-8的字串被當做GBK讀入了記憶體中。
解決這個亂碼問題有兩種方法,一是把test.txt用GBK儲存,則FileInputStream能正確讀入中文字元;二是使用InputStreamReader來轉換字元編碼,如:
InputStreamReader sr =new InputStreamReader(new FileInputStream(“D:\\test.txt”),“utf-8”);
BufferedReader reader =new BufferedReader(sr);
這樣,JAVA就會用utf-8的方式來從檔案中讀取字元資料。
另外,我們可以通過在java命令後帶上Dfile.encoding引數來指定虛擬機器讀取檔案使用的預設字元編碼,例如java -Dfile.encoding=utf-8 Test,這樣,我們在JAVA程式碼裡用System.getProperty(“file.encoding”)取到的值為utf-8。
四、JSP讀取request.getParameter裡的中文引數後,在頁面顯示為亂碼。
在JAVA的WEB應用中,對request物件裡的parameters的中文處理一直是常見也最難搞的一隻大怪獸。經常是剛搞定了這邊,那邊又出了亂碼。而導致這種複雜性的,主要是此過程中字元編解碼次數非常多,而且無論是瀏覽器還是WEB伺服器特別是TOMCAT總是不能給我們一個比較滿意的支援。
首先我們來分析用GET方式上傳引數的亂碼情況。
例如我們在瀏覽器位址列輸入以下URL:http://localhost:8080/test/test.jsp?param=大家好
我們的JSP程式碼如此處理param這個引數:
《% String text =request.getParameter(“param”); %》
《%=text%》
而就這麼簡單的兩句程式碼,我們很有可能在頁面上看到這樣的亂碼:?ó????
網上對處理request.getParamter中的亂碼有很多文章和方法,也都是正確的,只是方法太多讓人一直不明白到底是為什麼。這裡給大家分析一下到底是怎麼一回事。
首先,我們來看看與request物件有哪些相關的編碼設定:
1. JSP檔案的字元編碼
2. 請求這個帶引數URL的源頁面的字元編碼
3. IE的高階設定中的選項“總以utf-8方式傳送URL地址”
4. TOMCAT的server.xml中配置URIEncoding
5. 函式request.setCharacterEncoding()
6. JS的encodeURIComponent函式與JAVA的URLDecoder類
這麼多條相關編碼設定,也難怪大家被搞得頭暈了。這裡給大家根據各種情況給大家一一分析一下。
由這個表我們可以看到,IE的“總以utf-8方式傳送URL地址”設定並不影響對parameter的解析,而從頁面請求URL和從位址列輸入URL居然也有不同的表現。
根據這個表列出的現象,大家只要用smartSniff抓幾個網路包,並稍稍調查一下TOMCAT的原始碼,就可以得出以下結論:
1. IE設定中的“總以utf-8方式傳送URL地址”只對URL的PATH部分起作用,對查詢字串是不起作用的。也就是說,如果勾選了這個選項,那麼類似http://localhost:8080/test/大家好.jsp?param=大家好這種URL,前一個“大家好”將被轉化成utf-8形式,而後一個並沒有變化。這裡所說的utf-8形式,其實應該叫utf-8+escape形式,即%B4%F3%BC%D2%BA%C3這種形式。
那麼,查詢字串中的中文字元,到底是用什麼編碼傳送到伺服器的呢?答案是系統預設編碼,即GBK。也就是說,在我們中文作業系統上,傳送給WEB伺服器的查詢字串,總是以GBK來編碼的。
2. 在頁面中通過連結或location重定向或open新視窗的方式來請求一個URL,這個URL裡面的中文字元是用什麼編碼的?答:是用該頁面的編碼型別。也就是說,如果我們從某個源JSP頁面上的連結來訪問http://localhost:8080/test/test.jsp?param=大家好這個URL,如果源JSP頁面的編碼是UTF-8,則大家好這幾個字的編碼就是UTF-8。
而在位址列上直接輸入URL地址,或者從系統剪貼簿貼上到位址列上,這個輸入並非從頁面中發起的,而是由作業系統發起的,所以這個編碼只可能是系統的預設編碼,與任何頁面無關。我們還發現,在不同的瀏覽器上,用連結方式開啟的頁面,如果在位址列上再敲個回車,顯示的結果也會不同。IE上敲回車後顯示不變 化,而傲游上可能就會有亂碼或亂碼消失的變化。說明IE上敲回車,實際傳送的是之前記憶下來的記憶體中的URL,而傲游上傳送的從當前位址列重新獲取的 URL。
3. TOMCAT的URIEncoding如果不加以設定,則預設使用ISO-8859-1來解碼URL,設定後便用設定了的編碼方式來解碼。這個解碼同時包 括PATH部分和查詢字串部分。可見,這個引數是對用GET方式傳遞的中文引數最關鍵的設定。不過,這個引數只對GET方式傳遞的引數有效,對POST 的無效。分析TOMCAT的原始碼我們可以看到,在請求一個頁面時,TOMCAT會嘗試構造一個Request物件,在這個物件裡,會從 Server.xml裡讀取URIEncoding的值,並賦值給Parameters類的queryStringEncoding變數,而這個變數將在解析request.getParameter中的GET引數時用來指導字元解碼。
4. request.setCharacterEncoding函式只對POST的引數有效,對GET的引數無效。且這個函式必須是在第一次呼叫 request.getParameter之前使用。這是因為Parameters類有兩個字元編碼引數,一個是encoding,另一個是 queryStringEncoding,而setCharacterEncoding設定的是encoding,這個是在解析POST的引數是才用到的。
所以,這就導致了我們通常都要分開處理POST和GET的字元編碼,用TOMCAT自帶的filter只能處理POST的,另外要設置URIEncoding來設定GET的。這樣很麻煩而且URIEncoding無法根據內容來動態區分編碼,總還是一個問題。
在調查TOMCAT的程式碼時發現了另一個在server.xml裡的引數useBodyEncodingForURI,可以解決這個問題。這個引數設成 true後,TOMCAT就會用request.setCharacterEncoding所設定的字元編碼來同樣解析GET引數了。這樣,那個SetCharacterEncodingFilter就可以同時處理GET和POST引數了。
\
1. 亂碼
場合:頁面本身有中文的時候 解決辦法:servlet:resp.setContentType("text/html;charset=gbk"); Jsp: <%@ page contentType="text/html;charset=gb2312"%> 注意:一定要寫在PrintWriter out = resp.getWriter();之前 |
||
場合:解決get方式亂碼問題: 解決辦法:修改server.xml àURIEncoding="GBK" |
||
場合:解決post方式提交內容的亂碼 解決辦法:request.setCharacterEncoding("GBK"); 注意:一定要寫在存取第一個引數之前 不要呼叫response.setCharacterEncoding("GBK"); |
||
場合:<jsp:param name="user" value="<%=s%>"/>,url地址包含中文引數 解決辦法:<%request.setCharacterEncoding("GBK");%> 注意: |
||
字符集 |
字元編碼 |
對應語言 |
ASCII |
ASCII(7位) |
英語 |
ISO-8859-1 |
ISO-8859-1(8位) |
拉丁字母 |
GB2312 |
GB2312(16位) |
簡體中文 |
GBK |
GBK(16位) |
簡體中文 |
Unicode |
UTF-8(最多三個位元組) |
多國語言 |
http://blog.csdn.net/hoppboy/article/details/7027315