概述
世界上的各地区都有本地的语言。地区差异直接导致了语言环境的差异。在开发一个国际化程序的过程中,处理语言问题就显得很重要了。
这是一个世界范围内都存在的问题,所以,
Java
提供了世界性的解决方法。本文描述的方法是用于处理中文的,但是,推而广之,对于处理世界上其它国家和地区的语言同样适用。
汉字是双字节的。所谓双字节是指一个双字要占用两个
BYTE
的位置(即
16
位),分别称为高位和低位。中国规定的汉字编码为
GB2312
,这是强制性的,目前几乎所有的能处理中文的应用程序都支持
GB2312
。
GB2312
包括了一二级汉字和
9
区符号,高位从
0xa1
到
0xfe
,低位也是从
0xa1
到
0xfe
,其中,汉字的编码范围为
0xb0a1
到
0xf7fe
。
另外有一种编码,叫做
GBK
,但这是一份规范,不是强制的。
GBK
提供了
20902
个汉字,它兼容
GB2312
,编码范围为
0x8140
到
0xfefe
。
GBK
中的所有字符都可以一一映射到
Unicode 2.0
。
在不久的将来,中国会颁布另一种标准:
GB18030-2000
(
GBK2K
)。它收录了藏、蒙等少数民族的字型,从根本上解决了字位不足的问题。注意:它不再是定长的。其二字节部份与
GBK
兼容,四字节部分是扩充的字符、字形。它的首字节和第三字节从
0x81
到
0xfe
,二字节和第四字节从
0x30
到
0x39
。
本文不打算介绍
Unicode
,有兴趣的可以浏览“
http://www.unicode.org/
”查看更多的信息。
Unicode
有一个特性:它包括了世界上所有的字符字形。所以,各个地区的语言都可以建立与
Unicode
的映射关系,而
Java
正是利用了这一点以达到异种语言之间的转换。
在
JDK
中,与中文相关的编码有:
表1 JDK中与中文相关的编码列表
编码名称
|
说 明
|
ASCII
|
7
位,与
ascii7
相同
|
ISO8859-1
|
8-
位,与
8859_1,ISO-8859-1,ISO_8859-1,latin1...
等相同
|
GB2312-80
|
16
位,与
gb2312,gb2312-1980,EUC_CN,euccn,1381,Cp1381, 1383, Cp1383, ISO2022CN,ISO2022CN_GB...
等相同
|
GBK
|
与
MS936
相同,注意:区分大小写
|
UTF8
|
与
UTF-8
相同
|
GB18030
|
与
cp1392
、
1392
相同,目前支持的
JDK
很少
|
在实际编程时,接触得比较多的是
GB2312
(
GBK
)和
ISO8859-1
。
为什么会有“
?
”号
上文说过,异种语言之间的转换是通过
Unicode
来完成的。假设有两种不同的语言
A
和
B
,转换的步骤为:先把
A
转化为
Unicode
,再把
Unicode
转化为
B
。
举例说明。有
GB2312
中有一个汉字“李”,其编码为“
C0EE
”,欲转化为
ISO8859-1
编码。步骤为:先把“李”字转化为
Unicode
,得到“
674E
”,再把“
674E
”转化为
ISO8859-1
字符。当然,这个映射不会成功,因为
ISO8859-1
中根本就没有与“
674E
”对应的字符。
当映射不成功时,问题就发生了!当从某语言向
Unicode
转化时,如果在某语言中没有该字符,得到的将是
Unicode
的代码“
\uffffd
”(“
\u
”表示是
Unicode
编码,)。而从
Unicode
向某语言转化时,如果某语言没有对应的字符,则得到的是“
0x3f
”(“
?
”)。这就是“
?
”的由来。
例如:把字符流
buf =
“
0x80 0x40 0xb0 0xa1
”进行
new String(buf, "gb2312")
操作,得到的结果是“
\ufffd\u554a
”,再
println
出来,得到的结果将是“
?
啊”,因为“
0x80 0x40
”是
GBK
中的字符,在
GB2312
中没有。
再如,把字符串
String="\u00d6\u00ec\u00e9\u0046\u00bb\u00f9"
进行
new String (buf.getBytes("GBK"))
操作,得到的结果是“
3fa8aca8a6463fa8b4
”,其中,“
\u00d6
”在“
GBK
”中没有对应的字符,得到“
3f
”,“
\u00ec
”对应着“
a8ac
”,“
\u00e9
”对应着“
a8a6
”,“
0046
”对应着“
46
”(因为这是
ASCII
字符),“
\u00bb
”没找到,得到“
3f
”,最后,“
\u00f9
”对应着“
a8b4
”。把这个字符串
println
一下,得到的结果是“
?ìéF?ù
”。看到没?这里并不全是问号,因为
GBK
与
Unicode
映射的内容中除了汉字外还有字符,本例就是最好的明证。
所以,在汉字转码时,如果发生错乱,得到的不一定都是问号噢!不过,错了终究是错了,
50
步和
100
步并没有质的差别。
或者会问:如果源字符集中有,而
Unicode
中没有,结果会如何?回答是不知道。因为我手头没有能做这个测试的源字符集。但有一点是肯定的,那就是源字符集不够规范。在
Java
中,如果发生这种情况,是会抛出异常的。
什么是
UTF
UTF
,是
Unicode Text Format
的缩写,意为
Unicode
文本格式。对于
UTF
,是这样定义的:
(
1
)如果
Unicode
的
16
位字符的头
9
位是
0
,则用一个字节表示,这个字节的首位是“
0
”,剩下的
7
位与原字符中的后
7
位相同,如“
\u0034
”(
0000 0000 0011 0100
),用“
34
”
(0011 0100)
表示;(与源
Unicode
字符是相同的);
(
2
)如果
Unicode
的
16
位字符的头
5
位是
0
,则用
2
个字节表示,首字节是“
110
”开头,后面的
5
位与源字符中除去头
5
个零后的最高
5
位相同;第二个字节以“
10
”开头,后面的
6
位与源字符中的低
6
位相同。如“
\u025d
”(
0000 0010 0101 1101
),转化后为“
c99d
”(
1100 1001 1001 1101
);
(
3
)如果不符合上述两个规则,则用三个字节表示。第一个字节以“
1110
”开头,后四位为源字符的高四位;第二个字节以“
10
”开头,后六位为源字符中间的六位;第三个字节以“
10
”开头,后六位为源字符的低六位;如“
\u9da7
”(
1001 1101 1010 0111
),转化为“
e9b6a7
”(
1110 1001 1011 0110 1010 0111
);
可以这么描述
JAVA
程序中
Unicode
与
UTF
的关系,虽然不绝对:字符串在内存中运行时,表现为
Unicode
代码,而当要保存到文件或其它介质中去时,用的是
UTF
。这个转化过程是由
writeUTF
和
readUTF
来完成的。
好了,基础性的论述差不多了,下面进入正题。
先把这个问题想成是一个黑匣子。先看黑匣子的一级表示:
input(charsetA)->process(Unicode)->output(charsetB)
简单,这就是一个
IPO
模型,即输入、处理和输出。同样的内容要经过“从
charsetA
到
unicode
再到
charsetB
”的转化。
再看二级表示:
SourceFile(jsp,java)->class->output
在这个图中,可以看出,输入的是
jsp
和
java
源文件,在处理过程中,以
Class
文件为载体,然后输出。再细化到三级表示:
jsp->temp file->class->browser,os console,db
app,servlet->class->browser,os console,db
这个图就更明白了。
Jsp
文件先生成中间的
Java
文件,再生成
Class
。而
Servlet
和普通
App
则直接编译生成
Class
。然后,从
Class
再输出到浏览器、控制台或数据库等。
JSP
:从源文件到
Class
的过程
Jsp
的源文件是以“
.jsp
”结尾的文本文件。在本节中,将阐述
JSP
文件的解释和编译过程,并跟踪其中的中文变化。
1
、
JSP/Servlet
引擎提供的
JSP
转换工具(
jspc
)搜索
JSP
文件中用
<%@ page contentType ="text/html; charset=<Jsp-charset>"
%>
中指定的
charset
。如果在
JSP
文件中未指定
<Jsp-charset
>
,则取
JVM
中的默认设置
file.encoding
,一般情况下,这个值是
ISO8859-1
;
2
、
jspc
用相当于“
javac –encoding <Jsp-charset
>
”的命令解释
JSP
文件中出现的所有字符,包括中文字符和
ASCII
字符,然后把这些字符转换成
Unicode
字符,再转化成
UTF
格式,存为
JAVA
文件。
ASCII
码字符转化为
Unicode
字符时只是简单地在前面加“
00
”,如“
A
”,转化为“
\u0041
”(不需要理由,
Unicode
的码表就是这么编的)。然后,经过到
UTF
的转换,又变回“
41
”了!这也就是可以使用普通文本编辑器查看由
JSP
生成的
JAVA
文件的原因;
3
、引擎用相当于“
javac –encoding UNICODE
”的命令,把
JAVA
文件编译成
CLASS
文件;
先看一下这些过程中中文字符的转换情况。有如下源代码:
<%@ page contentType="text/html; charset=gb2312"%>
<html><body>
<%
String a="
中文
";
out.println(a);
%>
</body></html>
|
这段代码是在
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
文件中的完全一样。
再看
JSP
中指定的
CharSet
为
ISO-8859-1
的情况。
<%@ page contentType="text/html; charset=ISO-8859-1"%>
<html><body>
<%
String a="
中文
";
out.println(a);
%>
</body></html>
|
同样,该文件是用
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
”。
如果上述代码中不指定
<Jsp-charset
>
,即把第一行写成“
<%@ page contentType="text/html" %>
”,
JSPC
会使用
file.encoding
的设置来解释
JSP
文件。在
RedHat 6.2
上,其处理结果与指定为
ISO-8859-1
是完全相同的。
到现在为止,已经解释了从
JSP
文件到
CLASS
文件的转变过程中中文字符的映射过程。一句话:从“
JspCharSet
到
Unicode
再到
UTF
”。下表总结了这个过程:
表2 “中文”从JSP到CLASS的转化过程
Jsp-CharSet
|
JSP
文件中
|
JAVA
文件中
|
CLASS
文件中
|
GB2312
|
D6 D0 CE C4
(GB2312)
|
从
\u4E2D\u6587(Unicode)
到
E4 B8 AD E6 96 87 (UTF)
|
E4 B8 AD E6 96 87 (UTF)
|
ISO-8859-1
|
D6 D0 CE C4
(GB2312)
|
从
\u00D6\u00D0\u00CE\u00C4 (Unicode)
到
C3 96 C3 90 C3 8E C3 84 (UTF)
|
C3 96 C3 90 C3 8E C3 84 (UTF)
|
无(默认=
file.encoding
)
|
同
ISO-8859-1
|
同
ISO-8859-1
|
同
ISO-8859-1
|
下节先讨论
Servlet
从
JAVA
文件到
CLASS
文件的转化过程,然后再解释从
CLASS
文件如何输出到客户端。之所以这样安排,是因为
JSP
和
Servlet
在输出时处理方法是一样的。
Servlet
:从源文件到
Class
的过程
Servlet
源文件是以“
.java
”结尾的文本文件。本节将讨论
Servlet
的编译过程并跟踪其中的中文变化。
用“
javac
”编译
Servlet
源文件。
javac
可以带“
-encoding <Compile-charset
>
”参数,意思是“用
< Compile-charset
>
中指定的编码来解释
Serlvet
源文件”。
源文件在编译时,用
<Compile-charset
>
来解释所有字符,包括中文字符和
ASCII
字符。然后把字符常量转变成
Unicode
字符,最后,把
Unicode
转变成
UTF
。
在
Servlet
中,还有一个地方设置输出流的
CharSet
。通常在输出结果前,调用
HttpServletResponse
的
setContentType
方法来达到与在
JSP
中设置
<Jsp-charset
>
一样的效果,称之为
<Servlet-charset
>
。
注意,文中一共提到了三个变量:
<Jsp-charset
>
、
<Compile-charset
>
和
<Servlet-charset
>
。其中,
JSP
文件只与
<Jsp-charset
>
有关,而
<Compile-charset
>
和
<Servlet-charset
>
只与
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("<html>");
out.println("#
中文
#
");
out.println("</html>");
}
}
|
该文件也是用
UltraEdit for Windows
编写的,其中的“中文”两个字保存为“
D6 D0 CE C4
”(
GB2312
编码)。
开始编译。下表是
<Compile-charset
>
不同时,
CLASS
文件中“中文”两字的十六进制码。在编译过程中,
<Servlet-charset
>
不起任何作用。
<Servlet-charset
>
只对
CLASS
文件的输出产生影响,实际上是
<Servlet-charset
>
和
<Compile-charset
>
一起,达到与
JSP
文件中的
<Jsp-charset
>
相同的效果,因为
<Jsp-charset
>
对编译和
CLASS
文件的输出都会产生影响。
表3 “中文”从Servlet源文件到Class的转变过程
Compile-charset
|
Servlet
源文件中
|
Class
文件中
|
等效的
Unicode
码
|
GB2312
|
D6 D0 CE C4
(GB2312)
|
E4 B8 AD E6 96 87 (UTF)
|
\u4E2D\u6587 (
在
Unicode
中=“中文”
)
|
ISO-8859-1
|
D6 D0 CE C4
(GB2312)
|
C3 96 C3 90 C3 8E C3 84 (UTF)
|
\u00D6 \u00D0 \u00CE \u00C4 (
在
D6 D0 CE C4
前面各加了一个
00)
|
无(默认)
|
D6 D0 CE C4
(GB2312)
|
|
分享到:
相关推荐
本文将深入探讨JSP和Servlet如何处理中文字符,以及可能出现的乱码原因。 首先,中文字符是双字节编码,最常见的编码方式包括GB2312、GBK和GB18030。GB2312是早期的国家标准,包含了大部分常用汉字。GBK是对GB2312...
### 深入剖析JSP和Servlet对中文的处理 #### 一、引言 在全球化的今天,软件系统面临着多语言环境的挑战。特别是在Web应用中,如何正确地处理中文等非英文字符变得尤为重要。Java作为一种广泛使用的编程语言,提供...
### JSP & Servlet 深入剖析 #### JSP与Servlet技术概述 JSP(JavaServer Pages)与Servlet是Java Web开发中非常重要的两项技术,主要用于构建动态网页内容。这两种技术各有特点,能够满足不同场景下的需求。 ###...
本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。. 本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...
本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。. 本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...
本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。. 本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...
JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part21
本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。. 本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...
《图书管理系统:jsp+servlet+mysql的深度剖析与实践》 ...通过深入学习和实践这个系统,开发者不仅可以提升对jsp、servlet和mysql的理解,还能掌握Web开发的基本流程和技巧,为后续的项目开发打下坚实基础。
JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part42
JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part02
JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part08
JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part54
JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part46
JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part57
在探讨“jsp加servlet的问题”这一主题时,我们深入剖析JSP与Servlet在Web开发中的集成使用,以及它们如何协同工作以实现动态网页的高效处理。以下将详细阐述JSP与Servlet之间的交互机制、页面转发与重定向的区别,...
本文将围绕“JSP-Servlet中的汉字编码问题”这一主题展开讨论,通过对相关知识点的深入剖析,帮助读者理解JSP/Servlet环境中汉字编码可能出现的问题及解决方案。 #### 二、基础知识回顾 1. **字符编码**:字符编码...
本压缩包中的源代码涵盖了servlet和JSP的核心编程概念,包括多个章节的实例和解释,让我们一一剖析这些文件可能包含的内容: 1. MSAJSP-Styles.css:这通常是一个CSS(Cascading Style Sheets)文件,用于定义页面...