分享
 
 
 

剖析JSP和Servlet对中文的处理(1)

王朝java/jsp·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

Java程序员都曾遇到过这样的问题:输入的中文不能正确显示在界面上,保存在数据库中的也是一堆乱码,或者数据库或数据文件中存放的是正确的中文,可是在Java程序中看到的却是一大串的“?”。

这就是通常所说的“中文问题”。

Java中与中文相关的编码

在JDK中,提供了对大多数常用语言的支持。在解决“中文问题”时,表1中的编码是最常用,或者就是最有关系的。

表1

JDK中与中文相关的编码列表

在实际编程时,接触得比较多的是GB2312(GBK)和ISO8859-1。

注:UTF是Unicode Transformation Format的缩写,意为Unicode转换格式。可以这么描述JAVA程序中Unicode与UTF的关系,虽然不绝对。字符串在内存中运行时,表现为Unicode代码,而当要保存到文件或其它介质中去时,用的是UTF。这个转化过程是由writeUTF和readUTF来完成得。

Servlet/JSP对中文的处理过程

总体流程

把问题想成是一个黑匣子。先看黑匣子的一级表示(如图1所示):

图1 IPO模型

这就是一个IPO模型,即输入、处理和输出。同样的内容要经过“从charsetA到Unicode再到charsetB”的转化。

再看二级表示(如图2所示):

图2 JSP、Java输出模型

在这个图中,输入的是JSP和Java源文件。在处理过程中,以Class文件为载体,然后输出。再细化到三级(如图3所示):

图3 IPO模型

JSP文件先生成中间的Java文件,再生成Class。而Servlet和普通App则直接编译生成Class,然后,从Class再输出到浏览器、控制台或数据库等。

JSP:从源文件到Class的过程

JSP源文件是以“.jsp”结尾的文本文件。在本节中,将阐述JSP文件的解释和编译过程,并跟踪其中中文内容的变化。

一般地,JSP源文件经过如下步骤后变成可被引擎执行的Class文件:

1. JSP/Servlet引擎提供的JSP转换工具(JSPC)搜索JSP文件中用"%中指定的charset。如果在JSP文件中未指定,则默认为ISO8859-1(或者说是Latin-1)。

2. JSPC用相当于“Javac -encoding ”解释JSP文件中出现的所有字符,包括中文字符和ASCII字符。然后把这些字符转换成Unicode字符,再转化成UTF格式,存为Java文件。ASCII码字符转化为Unicode字符时只是简单地在前面加“00”,如“A”,转化为“\u0041”。然后,经过了UTF的转换,又变回“41”了。这也就是可以使用普通文本编辑器查看由JSP生成的Java文件的原因。

3. 引擎用相当于“Javac -encoding UTF-8”的命令,把Java文件编译成Class文件。

先看一下这些过程中中文字符的转换情况。有如下源代码:

String a="中文";

out.println(a);

%

这段代码是在UltraEdit for Windows上编写的。保存后,“中文”两个字的16进制编码为“D6 D0 CE C4”(GB2312编码)。经查表,“中文”两字的Unicode编码为“\u4E2D\u6587”,用 UTF表示就是“E4 B8 AD E6 96 87”。打开引擎生成的由JSP文件转变成的Java文件,发现其中的“中文”两个字的位置确实被“E4 B8 AD E6 96 87”替代了,再查看由Java文件编译生成的Class文件,发现结果与Java文件中的完全一样,也是“E4 B8 AD E6 96 87”。

再看JSP中指定的CharSet为ISO-8859-1的情况:

String a="中文";

out.println(a);

%

同样,该文件是用UltraEdit编写的。“中文”这两个字也是存为GB2312编码“D6 D0 CE C4”。先模拟一下生成的Java文件和Class文件的过程:JSPC用ISO-8859-1来解释“中文”,并把它映射到Unicode。由于ISO-8859-1是8位的,且是拉丁语系,其映射规则就是在每个字节前加“00”。所以,映射后的Unicode编码应为“\u00D6\u00D0\ u00CE\u00C4”,转化成UTF后应该是“C3 96 C3 90 C3 8E C3 84”。好,打开文件Java文件和CLASS文件,“中文”两个字的位置果然都表示为“C3 96 C3 90 C3 8E C3 84”。

如果上述代码中不指定,即把第一行写成“”,JSPC会使用默认的“ISO8859-1”来解释JSP文件。

到现在为止,已经解释了从JSP文件到Class文件的转变过程中中文字符的映射过程。一句话,从“Jsp-CharSet到Unicode再到UTF”。表2总结了这个过程:

表2

“中文”从JSP到Class的转化过程

Servlet:从源文件到Class的过程

Servlet源文件是以“.Java”结尾的文本文件。我们将讨论Servlet的编译过程并跟踪其中的中文变化。

用“Javac”编译Servlet源文件。Javac可以带“-encoding ”参数,意思是“用中指定的编码来解释Serlvet源文件”。

源文件在编译时,用来解释所有字符,包括中文字符和ASCII字符。然后把字符常量转变成Unicode字符。最后,把Unicode转变成UTF。

在Servlet中,还有一个地方设置输出流的CharSet。通常在输出结果前,调用HttpServletResponse的setContent Type方法来达到与在JSP中设置一样的效果,称之为。

注意:文中一共提到了三个变量:、和。其中,JSP文件只与有关,而和只与Servlet有关。

看下例:

import Javax.servlet.*;

import Javax.servlet.http.*;

Class testServlet extends HttpServlet

{

public void doGet(HttpServletRequest req,HttpServletResponse resp)

throws ServletException,Java.io.IOException

{

resp.setContentType("text/html; charset=GB2312");

Java.io.PrintWriter out=resp.getWriter();

out.println("");

out.println("#中文#");

out.println("");

}

}

该文件也是用UltraEdit for Windows编写的,其中的“中文”两个字保存为字节流“D6 D0 CE C4”(GB2312编码)。

开始编译。表3是不同时,Class文件中“中文”两字的十六进制码。在编译过程中,不起任何作用。只对Class文件的输出产生影响,可以说和一起,达到与JSP文件中的相同的效果,因为对编译过程和Class文件的输出都会产生影响。

表3

“中文”从Servlet源文件到Class的转变过程

注意:普通Java程序的编译过程与Servlet完全一样。

截止现在,从JSP或Servlet的源文件到Class文件的过程中中文内容的蜕变历程是不是昭然若揭了?OK,接下来看看Class文件中的中文又是怎样被输出的呢?

Class:输出字符串

Class文件是Java程序的一种存储载体。当Class文件被虚拟机执行时,通过readUTF把Class文件中的内容读入内存中。字符串在内存中表示为Unicode编码。当要把内存中的内容输出到别的程序或是外围设备(如终端)上去时,问题就来了(为了简单起见,把“别的程序或外围设备”称之为“输出对象”)。

1.如果输出对象能处理Unicode字符,则一切都很简单,只要把Unicode字符直接传给输出对象即可。

2.事实是,大多数输出对象不能直接处理Unicode,它们只能处理ISO8859-1和GB2312等。在往输出对象输出字符串时,需要做一定的转换才行。

看看下面的例子,给定一个有四个字符的Unicode字符串“00D6 00D0 00CE 00C4”,如果输出到只能识别“ISO8859-1”的程序中去,则直接去掉前面的“00”即可得到目的字符串“D6 D0 CE C4”。假如把它们输出到GB2312的程序中去,得到的结果很可能是一大堆乱码。因为在GB2312中可能没有(也有可能有)字符与00D6等字符对应(如果对应不上,将得到0x3f,也就是问号,如果对应上了,由于00D6等字符太靠前,估计也是一些特殊符号,真正的汉字在Unicode中的编码从4E00开始)。

同样的Unicode字符,输出到不同编码的对象中去时,结果是不同的。当然,这其中有一种是我们期望的结果。对于能处理中文的输出对象而言,自然希望输入的内容(也就是Java程序输出的内容)是基于GB2312编码有意义的中文字符串。

以上例而论,“D6 D0 CE C4”应该是我们所想要的。当把“D6 D0 CE C4”输出到IE中时,用“简体中文”方式查看,就能看到清楚的“中文”两个字了。

得出如下结论:

Java程序在输出字符串前,必须先把Unicode的字符串按照某一种内码重新生成字节流,然后把字节流输出给“输出对象”,相当于进行了一步“String.getBytes(???)”操作,其中???代表一种字符集的名字。

1.如果是Servlet,这种字符集是在HttpServlet Response.setContentType()方法中指定的,也就是上文定义的。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有