最近作網頁這塊時碰到了正文字符亂碼問題、別看這小小的一個問題,對我來講卻花費了好長一段時間。如今讓我慢慢分析它吧(說實話、這些有部分是從網上找的,但都是本身親自打出來的、這樣對本身來講不只理解了並且還加深了印象)。
在java內部運算中、涉及到全部字符串都會被轉化UTF-8編碼來運算,然而、在被java轉化以前,字符串是怎麼樣的字符集呢?其實java老是根據操做系統的默認編碼字符集來決定字符串的初始編碼的;並且java系統的輸入輸出都是採起操做系統的默認編碼。因此、咱們若是能統一java系統的輸入輸出以及操做系統三者的編碼字符集合,那麼就可以正確處理和顯示漢字。這也是處理java系統漢字的一個原則。
然而在實際項目中、可以正確抓住和控制住java系統的輸入輸出部分是比較難的。在J2EE中、因爲涉及到外部瀏覽器和數據庫等,中文亂碼問題很明顯。J2EE應用程序是運行在J2EE容器中的、在這個系統中,輸入途徑有多種:一種是經過頁面表單打包成請求(request)發往服務器的;第二種是經過數據庫讀入;而第三種輸入比較複雜,jsp在第一次運行時老是被編譯成Servlet,jsp中經常包含中文字符、那麼編譯使用javac時,java將根據默認的操做系統編碼做爲初始編碼;除非本身特別指定了編碼。
J2EE中的輸出途徑也有幾種:第一種是jsp頁面輸出、因爲jsp頁面已經被編譯成Servlet,那麼在輸出時、也將根據操做系統的默認編碼來選擇輸出編碼,除非指定輸出編碼方式;第二種輸出途徑即是將字符串輸出到數據庫。因此一個J2EE系統的輸入輸出是很是複雜、並且仍是動態變化的。而java是跨平臺運行的、在實際編譯和運行中,均可能涉及到不一樣的操做系統;若是任由java自由地根據操做系統來決定輸入輸出的編碼字符集、那將不可控制的出現亂碼了。
要處理此種狀況、根本辦法就是明確指定整個應用系統的統一字符集,指定統一字符集究竟是ISO8859_一、GBK、仍是UTF—8呢?下面咱們來分析下:
一、若是統一指定爲ISO8859_1,由於目前大多數軟件都是西方人編制的、他們默認的字符集就是ISO8859_1,包括Linux和數據庫MySQL等。因此咱們通常只須要注意在jsp頭部聲明、運行操做系統默認編碼以及開發和編譯代碼時指定字符集是否爲默認爲ISO8859_1。
二、統一指定爲GBK中文字符集、上面提到的三個地方須要一樣作到,不一樣的是隻能運行在編碼默認爲GBK的操做系統上、好比Windows;統一編碼爲ISO8859_1和GBK雖然能帶來編制代碼的方便、可是各自只能在相應的操做系統上運行;因此破壞了java跨平臺運行的優越性、在必定範圍內行得通;好比:爲了使得GBK編碼在linux上運行,設置Linux編碼爲GBK。
三、將java/j2EE系統的統一 編碼定義爲UTF-8,那麼除應用系統之外就不須要任何附加設置的中文編碼了。UTF-8是一種兼容全部語言的編碼方式,惟一比較麻煩的就是要找到應用系統的全部出入口;而後用UTF-8來更改它。
一個J2EE應用系統須要作下面幾項工做:
一、開發和編譯代碼時指定字符集爲UTF-8,JBuild(一種可視化java開發工具)和Ecplise均可以在項目屬性中設置。
二、使用過濾器、若是全部請求都通過一個Servlet控制分配器,那麼咱們使用Servlet時用filter執行語句,將全部來自瀏覽器的請求(request)轉換爲UTF-8;這是由於瀏覽器發過來的請求包括瀏覽器所在的操做系統編碼多是各類形式的編碼、咱們在他們數據流通的必經之地設置過濾:request.setCharacterEncoding(「UTF-8」);在使用filter時咱們須要配置web.xml裏文件來激活Filter。同時在jsp代碼中聲明UTF-八、設定數據庫鏈接方式也是UTF-8,好比鏈接mysql時的配置URL :jdbc:mysql://localhost:3306/test?useUnicode=true&charcaterEncoding=UTF-8;
下面看看java中文亂碼問題的由來吧:
Java的內核和class文件是基於unicode的,這使得java程序具備很好的跨平臺性;由此也帶來了一些中文亂碼問題。緣由主要有兩方面:java和jsp文件自己編譯時產生的亂碼問題和java程序於其餘媒介交互時產生的亂碼問題。
首先java(包括jsp)源文件中極可能包含有中文,而java和jsp源文件的保存方式是基於字節流的;若是java和jsp編譯成class文件過程當中、使用的編碼方式與源文件的編碼不一致,就會出現亂碼。對於處理這種亂碼、建議在java文件中儘可能不要寫中文,若是必須寫的話、儘可能手動帶參數-ecoding GBK 或者-ecoding gb2312編譯;對於jsp、在文件頭加上<%@ page contentType =」text/html;charset=GBK」%>或者是<%@ page contentType="text/html;charset=gb2312"%> 就能基本解決這類亂碼問題。
下面就來重點討論第二類亂碼問題吧、即java程序與其餘存儲媒介交互時產生的亂碼;不少存儲媒介如數據庫、文件、流等等的存儲方式都是基於字節流的;java程序與這類媒介交互時就會發生字符(char)與字節(byte)之間的轉換;具體狀況以下:
頁面form提交數據---->java程序(byte-->char)
java程序---->頁面數據顯示(char--->byte)
數據庫---->java程序(char----->byte)
Java程序---->數據庫(byte--->char)
文件--->java程序(byte-->char)
Java程序---->文件(char---->byte)
流--->java程序(byte---->char)
Java程序--->流(char--->byte)
若是以上轉換過程當中使用編碼方式與字節原有編碼不一致,極可能會出現亂碼。
解決方法:關鍵在於確保轉換時使用的編碼方式與字節原有的編碼方式保持一致。
一、jsp與頁面參數之間的亂碼
Jsp獲取頁面參數時通常採用系統默認的方式,若是頁面參數的編碼類型與系統默認的 編碼類型不一致、極可能就會出現亂碼。因此解決它的辦法是在頁面獲取參數以前,強制指定request獲取參數的編碼方式:request.setCharacterEncoding(「GBK」);若是在jsp將變量輸出到頁面時出現了亂碼、能夠經過設置request.setContentType(「text/html;charset=GBK」).若是不想在每一個文件裏都寫這樣兩句話,更簡潔的辦法是使用Servlet規範中的過慮器指定編碼,過濾器的在web.xml中的典型配置和主要代碼以下:
- <filter>
- <filter-name>CharacterEncodingFilter</filter-name>
- <filter-class>travel.web.CharacterEncodingFilter</filter-class>
- <init-param>
- <param-name>encoding</param-name>
- <param-value>GBK</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>CharacterEncodingFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- 過濾器中的代碼:
- public class CharacterEncodingFilter implements Filter {
-
- protected String encoding = null;
-
- public void init(FilterConfig filterConfig) throws ServletException {
-
- this.encoding = filterConfig.getInitParameter("encoding");
- }
-
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-
- request.setCharacterEncoding(encoding);response.setContentType("text/html;charset="+encoding);chain.doFilter(request, response);
-
- }
- }
二、java與數據庫之間的亂碼 大部分數據庫都支持以unicode編碼方式,因此解決Java與數據庫之間的亂碼問題比較明智的方式是直接使用unicode編碼與數據庫交互。不少數據庫驅動自動支持unicode,如Microsoft的SQLServer驅動。其餘大部分數據庫驅動,能夠在驅動的url參數中指定,如mysql驅動:jdbc:mysql://localhost/WEBCLDB?useUnicode=true&characterEncoding=GBK。 三、java與文件/流之間的亂碼 Java讀寫文件最經常使用的類是FileInputStream/FileOutputStream和FileReader/FileWriter。其中FileInputStream和FileOutputStream是基於字節流的,經常使用於讀寫二進制文件。讀寫字符文件建議使用基於字符的FileReader和FileWriter,省去了字節與字符之間的轉換。但這兩個類的構造函數默認使用系統的編碼方式,若是文件內容與系統編碼方式不一致,可能會出現亂碼。 在這種狀況下,建議使用FileReader和FileWriter的父類:InputStreamReader/OutputStreamWriter,它們也是基於字符的,但在構造函數中能夠指定編碼類型: InputStreamReader(InputStream in, Charset cs) 和OutputStreamWriter(OutputStream out, Charset cs)。 四、其餘 上面提到的方法應該能解決大部分亂碼問題,若是在其餘地方還出現亂碼,可能須要手動修改代碼。解決Java亂碼問題的關鍵在於在字節與字符的轉換過程當中,你必須知道原來字節或轉換後的字節的編碼方式,轉換時採用的編碼必須與這個編碼方式保持一致。