Java应用中文乱码成因分析及解决方法Word文件下载.docx
- 文档编号:16551449
- 上传时间:2022-11-24
- 格式:DOCX
- 页数:12
- 大小:117.53KB
Java应用中文乱码成因分析及解决方法Word文件下载.docx
《Java应用中文乱码成因分析及解决方法Word文件下载.docx》由会员分享,可在线阅读,更多相关《Java应用中文乱码成因分析及解决方法Word文件下载.docx(12页珍藏版)》请在冰豆网上搜索。
*Servlets类
*EJB类
*其它不可以直接运行的支持类
这些类文件中,都有可能含有中文字符串,并且我们常用前三类JAVA程序和用户直接交互,用于输出和输入字符,如:
我们在JSP和Servlet中得到客户端送来的字符,这些字符也包括中文字符。
无论这些JAVA类的作用如何,这些JAVA程序的生命周期都是这样的:
*编程人员在一定的操作系统上选择一个合适的【编辑软件】来实现源程序代码并以.java扩展名保存在操作系统中,例如我们在中文win2k中用记事本编辑一个java源程序;
*编程人员用JDK中的【编译器】javac.exe来编译这些源代码,形成.class类(JSP文件是由容器调用JDK来编译的);
*直接运行这些类或将这些类布署到WEB容器中通过Java【虚拟机】JVM去运行,并输出结果。
那么,在这些过程中,JDK和JVM是如何将这些文件如何编码和解码并运行的呢?
这里,我们以中文win2k操作系统为例说明JAVA类是如何来编码和被解码的。
第一步,我们在中文win2k中用编辑软件如记事本编写一个Java源程序文件(包括以上五类JAVA程序),程序文件在保存时默认采用了操作系统默认支持GBK编码格式(操作系统默认支持的格式为file.encoding格式)形成了一个.java文件,也即,java程序在被编译前,我们的JAVA源程序文件是采用操作系统默认支持的file.encoding编码格式保存的,java源程序中含有中文信息字符和英文程序代码;
要查看系统的file.encoding参数,可以用以下代码:
publicclassShowSystemDefaultEncoding{
publicstaticvoidmain(String[]args){
Stringencoding=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格式(用户可以设置输出流的编码格式)传递给操作系统显示接口并输出到界面上。
对于这种直接在console上运行的类,它的转化过程可用图1更加明确的表示出来:
图1
以上每一步的转化都需要正确的编码格式转化,才能最终不出现乱码现象。
b)EJB类和不可以直接运行的支持类(如JavaBean类)
由于EJB类和不可以直接运行的支持类,它们一般不与用户直接交互输入和输出,它们常常与其它的类进行交互输入和输出,所以它们在第二步被编译后,就形成了内容是UNICODE编码的类保存在操作系统中了,以后只要它与其它的类之间的交互在参数传递过程中没有丢失,则它就会正确的运行。
这种EJB类和不可以直接运行的支持类,它的转化过程可用图2更加明确的表示出来:
图2
c)JSP代码和Servlet类
经过第二步后,JSP文件也被转化为Servlets类文件,只不过它不像标准的Servlets一校存在于classes目录中,它存在于WEB容器的临时目录中,故这一步中我们也把它做为Servlets来看。
对于Servlets,客户端请求它时,WEB容器调用它的JVM来运行Servlet,首先,JVM把Servlet的class类从系统中读出并装入内存中,内存中是以UNICODE编码的Servlet类的代码,然后JVM在内存中运行该Servlet类,如果Servlet在运行的过程中,需要接受从客户端传来的字符如:
表单输入的值和URL中传入的值,此时如果程序中没有设定接受参数时采用的编码格式,则WEB容器会默认采用ISO-8859-1编码格式来接受传入的值并在JVM中转化为UNICODE格式的保存在WEB容器的内存中。
Servlet运行后生成输出,输出的字符串是UNICODE格式的,紧接着,容器将Servlet运行产生的UNICODE格式的串(如html语法,用户输出的串等)直接发送到客户端浏览器上并输出给用户,如果此时指定了发送时输出的编码格式,则按指定的编码格式输出到浏览器上,如果没有指定,则默认按ISO-8859-1编码发送到客户的浏览器上。
这种JSP代码和Servlet类,它的转化过程可用图3更加明确地表示出来:
图3
d)Java程序和数据库之间
对于几乎所有数据库的JDBC驱动程序,默认的在JAVA程序和数据库之间传递数据都是以ISO-8859-1为默认编码格式的,所以,我们的程序在向数据库内存储包含中文的数据时,JDBC首先是把程序内部的UNICODE编码格式的数据转化为ISO-8859-1的格式,然后传递到数据库中,在数据库保存数据时,它默认即以ISO-8859-1保存,所以,这是为什么我们常常在数据库中读出的中文数据是乱码。
对于JAVA程序和数据库之间的数据传递,我们可以用图4清晰地表示出来
图4
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来处理的。
4.中文问题的分类及其建议最优解决办法
了解以上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用于将字节流按照指定的字符编码集转换到字符流,如:
InputStreamReaderin=newInputStreamReader(System.in,"
GB2312"
);
OutputStreamWriterout=newOutputStreamWriter(System.out,"
例如:
采用如下的示例JAVA编码就达到了要求:
//Read.java
importjava.io.*;
publicclassRead{
publicstaticvoidmain(String[]args)throwsIOException{
Stringstr="
\n中文测试,这是内部硬编码的串"
+"
\ntestenglishcharacter"
;
Stringstrin="
"
BufferedReaderstdin=newBufferedReader(newInputStreamReader(System.in,"
gb2312"
));
//设置输入接口按中文编码
BufferedWriterstdout=newBufferedWriter(newOutputStreamWriter(System.out,"
//设置输出接口按中文编码
stdout.write("
请输入:
stdout.flush();
strin=stdin.readLine();
这是从用户输入的串:
+strin);
stdout.write(str);
}}
同时,在编译程序时,我们用以下方式来进行:
javac-encodinggb2312Read.java
其运行结果如图5所示:
图5
2)针对EJB类和不可以直接运行的支持类(如JavaBean类)
由于这种类它们本身被其它的类调用,不直接与用户交互,故对这种类来说,我们的建议的处理方式是内部程序中应该采用字符流来处理程序内部的中文字符串(具体如上面一节中一样),同时,在编译类时用-encodinggb2312参数指示源文件是中文格式编码的即可。
3)针对Servlet类
针对Servlet,我们建议用以下方法:
在编译Servlet类的源程序时,用-encoding指定编码为GBK或GB2312,且在向用户输出时的编码部分用response对象的setContentType("
text/html;
charset=GBK"
或gb2312来设置输出编码格式,同样在接收用户输入时,我们用request.setCharacterEncoding("
这样无论我们的servlet类移植到什么操作系统中,只有客户端的浏览器支持中文显示,就可以正确显示。
如下是一个正确的示例:
//HelloWorld.java
packagehello;
importjavax.servlet.*;
importjavax.servlet.http.*;
publicclassHelloWorldextendsHttpServlet
{
publicvoidinit()throwsServletException{}
publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsIOException,ServletException
request.setCharacterEncoding("
//设置输入编码格式
response.setContentType("
charset=GB2312"
//设置输出编码格式
PrintWriterout=response.getWriter();
//建议使用PrintWriter输出
out.println("
<
hr>
HelloWorld!
ThisiscreatedbyServlet!
测试中文!
}
publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsIOException,ServletException
Stringname=request.getParameter("
name"
Stringid=request.getParameter("
id"
if(name==null)name="
if(id==null)id="
你传入的中文字串是:
+name);
你输入的id是:
+id);
publicvoiddestroy(){}
请用javac-encodinggb2312HelloWorld.java来编译此程序。
测试此Servlet的程序如下所示:
<
%@pagecontentType="
charset=gb2312"
%>
%request.setCharacterEncoding("
html>
head>
title>
/title>
Scriptlanguage="
JavaScript"
>
functionSubmit(){
//通过URL传递中文字符串值给Servlet
document.base.action="
./HelloWorld?
name=中文"
document.base.method="
POST"
document.base.submit();
/Script>
/head>
bodybgcolor="
#FFFFFF"
text="
#000000"
topmargin="
5"
formname="
base"
method="
target="
_self"
inputname="
type="
text"
value="
size="
30"
ahref="
JavaScript:
Submit()"
传给Servlet<
/a>
/form>
/body>
/html>
其运行结果如图6所示:
图6
4)JAVA程序和数据库之间
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 应用 中文 乱码 成因 分析 解决方法