Java應用程序中System.out.println輸出中文亂碼

 

Java應用程序中System.out.println輸出中文亂碼

2013-12-10 21:27:08 cnblogs.com- bluepoint2009- 點擊數:3631
 

其實,解決辦法比較簡單,即:編譯時指定編碼爲UTF-8,如:php

1 javac -encoding utf-8 HelloJava.java


這樣,再運行時就不會出現亂碼html

 

1、中文問題的來源java

    計算機最初的操做系統支持的編碼是單字節的字符編碼,因而,在計算機中一切處理程序最初都是以單字節編碼的英文爲準進行處理。隨着計算機的發展,爲了適應世界其它民族的語言(固然包括咱們的漢字),人們提出了UNICODE編碼,它採用雙字節編碼,兼容英文字符和其它民族的雙字節字符編碼,因此,目前,大多數國際性的軟件內部均採用UNICODE編碼,在軟件運行時,它得到本地支持系統(多數時間是操做系統)默認支持的編碼格式,而後再將軟件內部的UNICODE轉化爲本地系統默認支持的格式顯示出來。JavaJDKJVM便是如此,我這裏說的JDK是指國際版的JDK,咱們大多數程序員使用的是國際化的JDK版本,如下全部的JDK均指國際化的JDK版本。咱們的漢字是雙字節編碼語言,爲了能讓計算機處理中文,咱們本身制定的gb2312GBKGBK2K等標準以適應計算機處理的需求。因此,大部分的操做系統爲了適應咱們處理中文的需求,均定製有中文操做系統,它們採用的是GBK,GB2312編碼格式以正確顯示咱們的漢字。如:中文Win2K默認採用的是GBK編碼顯示,在中文WIN2k中保存文件時默認採用的保存文件的編碼格式也是GBK的,即,全部在中文WIN2K中保存的文件它的內部編碼默認均採用GBK編碼,注意:GBK是在GB2312基礎上擴充來的。程序員

    因爲Java語言內部採用UNICODE編碼,因此在JAVA程序運行時,就存在着一個從UNICODE編碼和對應的操做系統及瀏覽器支持的編碼格式轉換輸入、輸出的問題,這個轉換過程有着一系列的步驟,若是其中任何一步出錯,則顯示出來的漢字就會出是亂碼,這就是咱們常見的JAVA中文問題。web

    同時,Java是一個跨平臺的編程語言,也即咱們編寫的程序不只能在中文windows上運行,也能在中文Linux等系統上運行,同時也要求能在英文等系統上運行(咱們常常看到有人把在中文win2k上編寫的JAVA程序,移植到英文Linux上運行)。這種移植操做也會帶來中文問題。數據庫

    還有,有人使用英文的操做系統和英文的IE等瀏覽器,來運行帶中文字符的程序和瀏覽中文網頁,它們自己就不支持中文,也會帶來中文問題。編程

    有,幾乎全部的瀏覽器默認在傳遞參數時都是以UTF-8編碼格式來傳遞,而不是按中文編碼傳遞,因此,傳遞中文參數時也會有問題,從而帶來亂碼現象。windows

    總之,以上幾個方面是JAVA中的中文問題的主要來源,咱們把以上緣由形成的程序不能正確運行而產生的問題稱做:JAVA中文問題。數組

2JAVA編碼轉換的詳細過程瀏覽器

    咱們常見的JAVA程序包括如下類別:

     *直接在console上運行的類(包括可視化界面的類)

     *JSP代碼類(注:JSPServlets類的變型)

     *Servelets

     *EJB

     *其它不能夠直接運行的支持類

    這些類文件中,都有可能含有中文字符串,而且咱們經常使用前三類JAVA程序和用戶直接交互,用於輸出和輸入字符,如:咱們在JSPServlet中獲得客戶端送來的字符,這些字符也包括中文字符。不管這些JAVA類的做用如何,這些JAVA程序的生命週期都是這樣的:

    *編程人員在必定的操做系統上選擇一個合適的編輯軟件來實現源程序代碼並以.java擴展名保存在操做系統中,例如咱們在中文win2k中用記事本編輯一個java源程序;

     *編程人員用JDK中的javac.exe來編譯這些源代碼,造成.class(JSP文件是由容器調用JDK來編譯的)

     *直接運行這些類或將這些類佈署到WEB容器中去運行,並輸出結果。

    那麼,在這些過程當中,JDKJVM是如何將這些文件如何編碼和解碼並運行的呢?

    這裏,咱們以中文win2k操做系統爲例說明JAVA類是如何來編碼和被解碼的。

    第一步,咱們在中文win2k中用編輯軟件如記事本編寫一個Java源程序文件(包括以上五類JAVA程序),程序文件在保存時默認採用了操做系統默認支持GBK編碼格式(操做系統默認支持的格式爲file.encoding格式)造成了一個.java文件,也即,java程序在被編譯前,咱們的JAVA源程序文件是採用操做系統默認支持的file.encoding編碼格式保存的,java源程序中含有中文信息字符和英文程序代碼;要查看系統的file.encoding參數,能夠用如下代碼:

public class ShowSystemDefaultEncoding {

public static void main(String[] args) {

String encoding = System.getProperty("file.encoding");

System.out.println(encoding);

}}

 

第二步,咱們用JDK的javac.exe文件編譯咱們的Java源程序,因爲JDK是國際版的,在編譯的時候,若是咱們沒有用-encoding參數指定咱們的JAVA源程序的編碼格式,則javac.exe首先得到咱們操做系統默認採用的編碼格式,也即在編譯java程序時,若咱們不指定源程序文件的編碼格式,JDK首先得到操做系統的file.encoding參數(它保存的就是操做系統默認的編碼格式,如WIN2k,它的值爲GBK),而後JDK就把咱們的java源程序從file.encoding編碼格式轉化爲JAVA內部默認的UNICODE格式放入內存中。而後,javac把轉換後的unicode格式的文件進行編譯成.class類文件,此時.class文件是UNICODE編碼的,它暫放在內存中,緊接着,JDK將此以UNICODE編碼的編譯後的class文件保存到咱們的操做系統中造成咱們見到的.class文件。對咱們來講,咱們最終得到的.class文件是內容以UNICODE編碼格式保存的類文件,它內部包含咱們源程序中的中文字符串,只不過此時它己經由file.encoding格式轉化爲UNICODE格式了。

 

    這一步中,對於JSP源程序文件是不一樣的,對於JSP,這個過程是這樣的:即WEB容器調用JSP編譯器,JSP編譯器先查看JSP文件中是否設置有文件編碼格式,若是JSP文件中沒有設置JSP文件的編碼格式,則JSP編譯器調用JDK先把JSP文件用JVM默認的字符編碼格式(也即WEB容器所在的操做系統的默認的file.encoding)轉化爲臨時的Servlet類,而後再把它編譯成UNICODE格式的class類,並保存在臨時文件夾中。如:在中文win2k上,WEB容器就把JSP文件從GBK編碼格式轉化爲UNICODE格式,而後編譯成臨時保存的Servlet類,以響應用戶的請求。

    第三步,運行第二步編譯出來的類,分爲三種狀況:

    A、 直接在console上運行的類

    B、 EJB類和不能夠直接運行的支持類(如JavaBean類)

    C、 JSP代碼和Servlet類

    D、 JAVA程序和數據庫之間

    下面咱們分這四種狀況來看。

    A、直接在console上運行的類

    這種狀況,運行該類首先須要JVM支持,即操做系統中必須安裝有JRE。運行過程是這樣的:首先java啓動JVM,此時JVM讀出操做系統中保存的class文件並把內容讀入內存中,此時內存中爲UNICODE格式的class類,而後JVM運行它,若是此時此類須要接收用戶輸入,則類會默認用file.encoding編碼格式對用戶輸入的串進行編碼並轉化爲unicode保存入內存(用戶能夠設置輸入流的編碼格式)。程序運行後,產生的字符串(UNICODE編碼的)再回交給JVM,最後JRE把此字符串再轉化爲file.encoding格式(用戶能夠設置輸出流的編碼格式)傳遞給操做系統顯示接口並輸出到界面上。

   以上每一步的轉化都須要正確的編碼格式轉化,才能最終不出現亂碼現象。

    B、EJB類和不能夠直接運行的支持類(如JavaBean類)

    因爲EJB類和不能夠直接運行的支持類,它們通常不與用戶直接交互輸入和輸出,它們經常與其它的類進行交互輸入和輸出,因此它們在第二步被編譯後,就造成了內容是UNICODE編碼的類保存在操做系統中了,之後只要它與其它的類之間的交互在參數傳遞過程當中沒有丟失,則它就會正確的運行。

    C、JSP代碼和Servlet類

    通過第二步後,JSP文件也被轉化爲Servlets類文件,只不過它不像標準的Servlets一校存在於classes目錄中,它存在於WEB容器的臨時目錄中,故這一步中咱們也把它作爲Servlets來看。

    對於Servlets,客戶端請求它時,WEB容器調用它的JVM來運行Servlet,首先,JVMServletclass類從系統中讀出並裝入內存中,內存中是以UNICODE編碼的Servlet類的代碼,而後JVM在內存中運行該Servlet類,若是Servlet在運行的過程當中,須要接受從客戶端傳來的字符如:表單輸入的值和URL中傳入的值,此時若是程序中沒有設定接受參數時採用的編碼格式,則WEB容器會默認採用ISO-8859-1編碼格式來接受傳入的值並在JVM中轉化爲UNICODE格式的保存在WEB容器的內存中。Servlet運行後生成輸出,輸出的字符串是UNICODE格式的,緊接着,容器將Servlet運行產生的UNICODE格式的串(如html語法,用戶輸出的串等)直接發送到客戶端瀏覽器上並輸出給用戶,若是此時指定了發送時輸出的編碼格式,則按指定的編碼格式輸出到瀏覽器上,若是沒有指定,則默認按ISO-8859-1編碼發送到客戶的瀏覽器上。

      DJava程序和數據庫之間

    對於幾乎全部數據庫的JDBC驅動程序,默認的在JAVA程序和數據庫之間傳遞數據都是以ISO-8859-1爲默認編碼格式的,因此,咱們的程序在向數據庫內存儲包含中文的數據時,JDBC首先是把程序內部的UNICODE編碼格式的數據轉化爲ISO-8859-1的格式,而後傳遞到數據庫中,在數據庫保存數據時,它默認即以ISO-8859-1保存,因此,這是爲何咱們經常在數據庫中讀出的中文數據是亂碼。

3、分析常見的JAVA中文問題幾個必須清楚的原則

    首先,通過上面的詳細分析,咱們能夠清晰地看到,任何JAVA程序的生命期中,其編碼轉換的關鍵過程是在於:最初編譯成class文件的轉碼和最終向用戶輸出的轉碼過程。

 

 

 

 

 

其次,咱們必須瞭解JAVA在編譯時支持的、經常使用的編碼格式有如下幾種:

    *ISO-8859-1,8-bit, 同8859_1,ISO-8859-1,ISO_8859_1等編碼

    *Cp1252,美國英語編碼,同ANSI標準編碼

    *UTF-8,同unicode編碼

    *GB2312,同gb2312-80,gb2312-1980等編碼

    *GBK , 同MS936,它是gb2312的擴充

    及其它的編碼,如韓文、日文、繁體中文等。同時,咱們要注意這些編碼間的兼容關體系以下:

    unicode和UTF-8編碼是一一對應的關係。GB2312能夠認爲是GBK的子集,即GBK編碼是在gb2312上擴展來的。同時,GBK編碼包含了20902個漢字,編碼範圍爲:0x8140-0xfefe,全部的字符能夠一一對應到UNICODE2.0中來。

    再次,對於放在操做系統中的.java源程序文件,在編譯時,咱們能夠指定它內容的編碼格式,具體來講用-encoding來指定。注意:若是源程序中含有中文字符,而你用-encoding指定爲其它的編碼字符,顯然是要出錯的。用-encoding指定源文件的編碼方式爲GBK或gb2312,不管咱們在什麼系統上編譯含有中文字符的JAVA源程序都不會有問題,它都會正確地將中文轉化爲UNICODE存儲在class文件中。

    而後,咱們必須清楚,幾乎全部的WEB容器在其內部默認的字符編碼格式都是以ISO-8859-1爲默認值的,同時,幾乎全部的瀏覽器在傳遞參數時都是默認以UTF-8的方式來傳遞參數的。因此,雖然咱們的Java源文件在出入口的地方指定了正確的編碼方式,但其在容器內部運行時仍是以ISO-8859-1來處理的。

四、中文問題的分類及其建議最優解決辦法

    瞭解以上JAVA處理文件的原理以後,咱們就能夠提出了一套建議最優的解決漢字問題的辦法。

    咱們的目標是:咱們在中文系統中編輯的含有中文字符串或進行中文處理的JAVA源程序經編譯後能夠移值到任何其它的操做系統中正確運行,或拿到其它操做系統中編譯後能正確運行,能正確地傳遞中文和英文參數,能正確地和數據庫交流中英文字符串。

    咱們的具體思路是:在JAVA程序轉碼的入口和出口及JAVA程序同用戶有輸入輸出轉換的地方限制編碼方法使之正確便可。

    具體解決辦法以下:

    1、 針對直接在console上運行的類

    對於這種狀況,咱們建議在程序編寫時,若是須要從用戶端接收用戶的可能含有中文的輸入或含有中文的輸出,程序中應該採用字符流來處理輸入和輸出,具體來講,應用如下面向字符型節點流類型:

    對文件:FileReader,FileWrieter

        其字節型節點流類型爲:FileInputStream,FileOutputStream

    對內存(數組):CharArrayReader,CharArrayWriter

        其字節型節點流類型爲:ByteArrayInputStream,ByteArrayOutputStream

    對內存(字符串):StringReader,StringWriter

    對管道:PipedReader,PipedWriter

        其字節型節點流類型爲:PipedInputStream,PipedOutputStream

    同時,應該用如下面向字符型處理流來處理輸入和輸出:

    BufferedWriter,BufferedReader

        其字節型的處理流爲:BufferedInputeStream,BufferedOutputStream

    InputStreamReader,OutputStreamWriter

    其字節型的處理流爲:DataInputStream,DataOutputStream

    其中InputStreamReader和InputStreamWriter用於將字節流按照指定的字符編碼集轉換到字符流,如:

    InputStreamReader in = new InputStreamReader(System.in,"GB2312");

    OutputStreamWriter out = new OutputStreamWriter (System.out,"GB2312");

    例如:採用以下的示例JAVA編碼就達到了要求:

//Read.java

import java.io.*;

public class Read {

public static void main(String[] args) throws IOException {

String str = " 中文測試,這是內部硬編碼的串"+" test english character";

String strin= "";

BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in,"gb2312")); //設置輸入接口按中文編碼

BufferedWriter stdout = new BufferedWriter(new OutputStreamWriter(System.out,"gb2312")); //設置輸出接口按中文編碼

stdout.write("請輸入:");

stdout.flush();

strin = stdin.readLine();

stdout.write("這是從用戶輸入的串:"+strin);

stdout.write(str);

stdout.flush();

}}

    同時,在編譯程序時,咱們用如下方式來進行:

    javac -encoding gb2312 Read.java

    2、 針對EJB類和不能夠直接運行的支持類(如JavaBean類)

    因爲這種類它們自己被其它的類調用,不直接與用戶交互,故對這種類來講,咱們的建議的處理方式是內部程序中應該採用字符流來處理程序內部的中文字符串(具體如上面一節中同樣),同時,在編譯類時用-encoding gb2312參數指示源文件是中文格式編碼的便可。

    3、 針對Servlet類

    針對Servlet,咱們建議用如下方法:

    在編譯Servlet類的源程序時,用-encoding指定編碼爲GBKGB2312,且在向用戶輸出時的編碼部分用response對象的setContentType("text/html;charset=GBK");gb2312來設置輸出編碼格式,一樣在接收用戶輸入時,咱們用request.setCharacterEncoding("GB2312");這樣不管咱們的servlet類移植到什麼操做系統中,只有客戶端的瀏覽器支持中文顯示,就能夠正確顯示。以下是一個正確的示例:

//HelloWorld.java

package hello;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class HelloWorld extends HttpServlet

{

public void init() throws ServletException { }

public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException

{

request.setCharacterEncoding("GB2312"); //設置輸入編碼格式

response.setContentType("text/html;charset=GB2312"); //設置輸出編碼格式

PrintWriter out = response.getWriter(); //建議使用PrintWriter輸出

out.println("<hr>");

out.println("Hello World! This is created by Servlet!測試中文!");

out.println("<hr>");

}

public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException

{

request.setCharacterEncoding("GB2312"); //設置輸入編碼格式

response.setContentType("text/html;charset=GB2312"); //設置輸出編碼格式

String name = request.getParameter("name");

String id = request.getParameter("id");

if(name==null) name="";

if(id==null) id="";

PrintWriter out = response.getWriter(); //建議使用PrintWriter輸出

out.println("<hr>");

out.println("你傳入的中文字串是:" + name);

out.println("<hr>你輸入的id是:" + id);

out.println("<hr>");

}

public void destroy() { }

}

    請用javac -encoding gb2312 HelloWorld.java來編譯此程序。

    測試此Servlet的程序以下所示:

<%@page contentType="text/html; charset=gb2312"%>

<%request.setCharacterEncoding("GB2312");%>

<html><head><title></title>

<Script language="JavaScript">

function Submit() {

//經過URL傳遞中文字符串值給Servlet

document.base.action = "./HelloWorld?name=中文";

document.base.method = "POST";

document.base.submit();

}

</Script>

</head>

<body bgcolor="#FFFFFF" text="#000000" topmargin="5">

<form name="base" method = "POST" target="_self">

<input name="id" type="text" value="" size="30">

<a href = "JavaScript:Submit()">傳給Servlet</a>

</form></body></html>

 

 

//testchinese.jsp

<%@page pageEncoding="GB2312"%>

<%@page contentType="text/html; charset=gb2312"%>

<%request.setCharacterEncoding("GB2312");%>

<%

String action = request.getParameter("ACTION");

String name = "";

String str = "";

if(action!=null && action.equals("SENT"))

{

name = request.getParameter("name");

str = request.getParameter("str");

}

%>

<html>

<head>

<title></title>

<Script language="JavaScript">

function Submit()

{

document.base.action = "?ACTION=SENT&str=傳入的中文";

document.base.method = "POST";

document.base.submit();

}

</Script>

</head>

<body bgcolor="#FFFFFF" text="#000000" topmargin="5">

<form name="base" method = "POST" target="_self">

<input type="text" name="name" value="" size="30">

<a href = "JavaScript:Submit()">提交</a>

</form>

<%

if(action!=null && action.equals("SENT"))

{

out.println("<br>你輸入的字符爲:"+name);

out.println("<br>你經過URL傳入的字符爲:"+str);

}

%>

</body>

</html>

 

因爲大多數本地測試環境是TOMCAT,現也將其中文問題一併附上。

Tomcat中文問題- -

  tomcat5中發現了之前處理tomcat4的方法不能適用於處理直接經過url提交的請求,上網找資料終於發現了最完美的解決辦法,不用每一個地方都轉換了,並且不管get,post都正常。寫了個文檔,貼出來但願跟我有一樣問題的人再也不像我同樣痛苦一次:-)

問題描述:

1 表單提交的數據,用request.getParameter(xxx)返回的字符串爲亂碼或者??

2 直接經過urlhttp://localhost/a.jsp?name=中國,這樣的get請求在服務端用request. getParameter(name)時返回的是亂碼;按tomcat4的作法設置Filter也沒有用或者用request.setCharacterEncoding("GBK");也無論用

緣由:

1 tomcatj2ee實現對錶單提交即post方式提示時處理參數採用缺省的iso-8859-1來處理

2 tomcatget方式提交的請求對query-string 處理時採用了和post方法不同的處理方式。(tomcat4不同,因此設置setCharacterEncoding(gbk))不起做用。

解決辦法:

首先全部的jsp文件都加上:

1 實現一個Filter.設置處理字符集爲GBK(tomcatwebapps/servlet-examples目錄有一個完整的例子。請參考web.xmlSetCharacterEncodingFilter的配置。)

 

1)只要把%TOMCAT安裝目錄%/ webappsservlets-examplesWEB-INFclassesfiltersSetCharacterEncodingFilter.class文件拷到你的webapp目錄/filters下,若是沒有filters目錄,就建立一個。

2)在你的web.xml里加入以下幾行: <filter>

<filter-name>Set Character Encoding</filter-name>

<filter-class>filters.SetCharacterEncodingFilter</filter-class>

<init-param>

<param-name>encoding</param-name>

<param-value>GBK</param-value>

</init-param>

</filter>

  <filter-mapping>

<filter-name>Set Character Encoding</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

3)完成.

2 get方式的解決辦法

1) 打開tomcatserver.xml文件,找到區塊,加入以下一行:

URIEncoding=」GBK」

完整的應以下:

<Connector

port="80" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"

enableLookups="false" redirectPort="8443" acceptCount="100"

debug="0" connectionTimeout="20000"

disableUploadTimeout="true"

URIEncoding="GBK"

/>

2)重啓tomcat,一切OK

執行以下jsp頁頁測試是否成功

<%@ page contentType="text/html;charset=gb2312"%>

<%@ page import="java.util.*"%>

<%

  String q=request.getParameter("q");

q = q == null? "沒有值" : q;

%>

<HTML>

<HEAD><TITLE>新聞列表顯示</TITLE>

<META http-equiv=Content-Type content="text/html; charset=gb2312">

<META http-equiv=pragma content=no-cache>

<body>

你提交了:

<%=q%>

<br>

<form action="tcnchar.jsp" method="post">

輸入中文:<input type="text" name="q"><input type="submit" value="肯定">

<br>

<a href="tcnchar.jsp?q=中國">經過get方式提交</a>

</form>

</BODY></HTML>

測試結果若是你輸入文本框或者點超鏈都會顯示:你提交了」中國」,說明成功!!!!!       

相關文章
相關標籤/搜索