8 W5 G; ^ V9 F. U c7 s' l 2、jspc用相当于“javac –encoding <Jsp-charset>”的命令解释JSP文件中出现的所有字符,包括中文字符和ASCII字符,然后把这些字符转换成Unicode字符,再转化成UTF格式,存为JAVA文件。ASCII码字符转化为Unicode字符时只是简单地在前面加“00”,如“A”,转化为“\u0041”(不需要理由,Unicode的码表就是这么编的)。然后,经过到UTF的转换,又变回“41”了!这也就是可以使用普通文本编辑器查看由JSP生成的JAVA文件的原因;. Z% j* z; M9 D- @/ a7 x
! u% q. s' Q* o$ n, ~6 ?
3、引擎用相当于“javac –encoding UNICODE”的命令,把JAVA文件编译成CLASS文件; 0 d W5 n8 i8 d. T+ {2 ]- |; Y9 h) C6 c1 y. t
先看一下这些过程中中文字符的转换情况。有如下源代码: , x; g, l1 _8 z 8 M2 o# x. V& {7 c& M; J4 F( Y9 p<%@ page contentType="text/html; charset=gb2312"%> % |2 v' A ^+ n+ x<html><body> : K( T, a% d; F3 Q v2 v<% : V' O/ i& E7 e; H String a="中文";1 u: Q; u5 L: n; E. V: }
out.println(a); 3 Q6 k7 |/ B! d9 K5 z0 y%>% l0 O d' C1 E5 W9 \9 o9 \
</body></html>0 `& i9 a. h* P2 ~5 p; `+ 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文件中的完全一样。 - ^' O3 ]1 ^. }1 \ / J- h* j c$ k" }0 b9 o2 I 再看JSP中指定的CharSet为ISO-8859-1的情况。% k( U, \2 [2 h1 s/ o7 T" E5 Q
' \& g- I* {, q ( M( L2 Z6 t; o1 X<%@ page contentType="text/html; charset=ISO-8859-1"%> ; n, K0 c6 N' n8 x; `<html><body> 6 ]3 d4 p) r, l% T8 C4 C _<%% R! \0 f' v7 Y9 n$ |8 h8 O
String a="中文";! \& |! r# N/ M0 G* L
out.println(a); 0 x0 D' o( \' b3 C* u7 F9 y0 I%> 2 O5 A8 s: E! y6 k9 j- o& s; N# m</body></html> & e0 ~: l4 e- |; D) ~1 h 同样,该文件是用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”。* v( n; r6 k) F/ j: E. Z
: H" H8 \% P9 ~0 U3 u% |
如果上述代码中不指定<Jsp-charset>,即把第一行写成“<%@ page contentType="text/html" %>”,JSPC会使用file.encoding的设置来解释JSP文件。在RedHat 6.2上,其处理结果与指定为ISO-8859-1是完全相同的。 % c0 g. S7 R1 ~3 k" d( u) _. O' \ : t# {- v8 k- a 到现在为止,已经解释了从JSP文件到CLASS文件的转变过程中中文字符的映射过程。一句话:从“JspCharSet到Unicode再到UTF”。下表总结了这个过程:$ t. z5 X% d+ G! G( q" M. c
8 [3 |0 N9 v( J+ ^' S5 q, Y. R 表2 “中文”从JSP到CLASS的转化过程 " ]7 t1 w6 l; D$ c Y7 R# o( J: E! L # P( O5 y" [) C6 A$ r& ?* J& ~Jsp-CharSetJSP文件中JAVA文件中CLASS文件中GB2312D6 D0 CE C4(GB2312)从\u4E2D\u6587(Unicode)到E4 B8 AD E6 96 87 (UTF)E4 B8 AD E6 96 87 (UTF)ISO-8859-1D6 D0 CE C4- u# | m1 @/ K7 w3 i/ |( \
(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 5 h; r3 h: n0 k 下节先讨论Servlet从JAVA文件到CLASS文件的转化过程,然后再解释从CLASS文件如何输出到客户端。之所以这样安排,是因为JSP和Servlet在输出时处理方法是一样的。作者: 韩冰 时间: 2004-11-21 12:04
Servlet:从源文件到Class的过程 ! O; Y( T0 o6 r2 d. C( d [ ( s0 A' c, I. a6 J; d Servlet源文件是以“.java”结尾的文本文件。本节将讨论Servlet的编译过程并跟踪其中的中文变化。 : j9 A6 }' T/ e5 m/ Y4 g( E" b5 ? " [6 R1 s+ h: }3 j6 i 用“javac”编译Servlet源文件。javac可以带“-encoding <Compile-charset>”参数,意思是“用< Compile-charset >中指定的编码来解释Serlvet源文件”。' x# a# P7 ^' `& T: M O& T( M
3 X$ E! ^/ N+ K& g+ o 源文件在编译时,用<Compile-charset>来解释所有字符,包括中文字符和ASCII字符。然后把字符常量转变成Unicode字符,最后,把Unicode转变成UTF。 $ c1 A7 Y6 N0 w5 b# D 9 S' e) j$ O9 P- H0 g9 P- \$ c 在Servlet中,还有一个地方设置输出流的CharSet。通常在输出结果前,调用HttpServletResponse的setContentType方法来达到与在JSP中设置<Jsp-charset>一样的效果,称之为<Servlet-charset>。 " Z/ l1 w+ x+ i& W( h' V5 p0 z- ?% a+ K- @! L1 L6 L& M
注意,文中一共提到了三个变量:<Jsp-charset>、<Compile-charset>和<Servlet-charset>。其中,JSP文件只与<Jsp-charset>有关,而<Compile-charset>和<Servlet-charset>只与Servlet有关。 : j4 S; }& P7 N# C& ~' X- _$ n
看下例: g4 W0 M9 g8 v1 l! b( m( @ p" Y" M4 n9 S7 k7 p' w+ x* Vimport javax.servlet.*;& u$ o9 X) h' _5 n' C
$ ]0 i' E+ w. b4 e+ o
import javax.servlet.http.*; 5 |, G& P' F% G4 l5 K8 v4 C ! d1 u( Q3 D5 P8 o6 ^class testServlet extends HttpServlet 4 L5 f* [6 x X- d& [/ T0 V{. ^6 d) M# h7 B( i1 ~& q
public void doGet(HttpServletRequest req,HttpServletResponse resp) $ b: H$ l) O& n% l( \5 ]5 \ throws ServletException,java.io.IOException% Q# _; P. D6 ?( B
{% H* l* ~: x8 H' O, n- s
resp.setContentType("text/html; charset=GB2312"); & `4 u3 q' ^5 c java.io.PrintWriter out=resp.getWriter();0 [% {" I3 b( I# F }
out.println("<html>"); 5 p7 T& B; C- v+ a( F out.println("#中文#"); 5 ?$ H' u7 v& h. d$ R out.println("</html>"); ' z. H4 ?) v9 o3 @( s4 C' @ } ' ^/ r, F& Y& F' \# E, T}3 r1 l9 u8 T* Y
该文件也是用UltraEdit for Windows编写的,其中的“中文”两个字保存为“D6 D0 CE C4”(GB2312编码)。6 B: m- n' w6 ]
: D% ~) @/ D1 N4 P- A 开始编译。下表是<Compile-charset>不同时,CLASS文件中“中文”两字的十六进制码。在编译过程中,<Servlet-charset>不起任何作用。<Servlet-charset>只对CLASS文件的输出产生影响,实际上是<Servlet-charset>和<Compile-charset>一起,达到与JSP文件中的<Jsp-charset>相同的效果,因为<Jsp-charset>对编译和CLASS文件的输出都会产生影响。9 C+ y. a* {& L3 E |: u, V
+ V" y5 \7 H# Z
表3 “中文”从Servlet源文件到Class的转变过程 + I& {8 c1 b' k5 S1 D # G' \3 t. F" Y, d+ Z' \Compile-charsetServlet源文件中Class文件中等效的Unicode码GB2312D6 D0 CE C4 : u4 V# v0 F. o5 }& _
(GB2312)E4 B8 AD E6 96 87 (UTF)\u4E2D\u6587 (在Unicode中=“中文”)ISO-8859-1D6 D0 CE C4 . c$ ^: |) T, p6 M2 R9 k+ |
(GB2312)C3 96 C3 90 C3 8E C3 84 (UTF)\u00D6 \u00D0 \u00CE \u00C4 (在D6 D0 CE C4前面各加了一个00)无(默认)D6 D0 CE C4 (GB2312)同ISO-8859-1同ISO-8859-1( Y, S/ O9 I, Z2 A3 t
普通Java程序的编译过程与Servlet完全一样。 $ E4 W1 H: S- L* J9 c# ]5 K/ X$ t- }3 I, ~9 s& B8 Q" i% P
CLASS文件中的中文表示法是不是昭然若揭了?OK,接下来看看CLASS又是怎样输出中文的呢? # [- ~9 f9 c8 z4 {7 M6 r: _2 a; I; C) n1 e
Class:输出字符串* M1 ~+ H; @9 U( N
7 b" Y: P8 ? z+ W" l \
上文说过,字符串在内存中表现为Unicode编码。至于这种Unicode编码表示了什么,那要看它是从哪种字符集映射过来的,也就是说要看它的祖先。这好比在托运行李时,外观都是纸箱子,里面装了什么就要看寄邮件的人实际邮了什么东西。1 V* V" Z# c9 Z+ F+ |. D
# D* E3 g8 T+ t4 p% g 看看上面的例子,如果给一串Unicode编码“00D6 00D0 00CE 00C4”,如果不作转换,直接用Unicode码表来对照它时,是四个字符(而且是特殊字符);假如把它与“ISO8859-1”进行映射,则直接去掉前面的“00”即可得到“D6 D0 CE C4”,这是ASCII码表中的四个字符;而假如把它当作GB2312来进行映射,得到的结果很可能是一大堆乱码,因为在GB2312中有可能没有(也有可能有)字符与00D6等字符对应(如果对应不上,将得到0x3f,也就是问号,如果对应上了,由于00D6等字符太靠前,估计也是一些特殊符号,真正的汉字在Unicode中的编码从4E00开始)。- U5 D$ j. h' `4 W
0 K L4 C4 G* O; c 各位看到了,同样的Unicode字符,可以解释成不同的样子。当然,这其中有一种是我们期望的结果。以上例而论,“D6 D0 CE C4”应该是我们所想要的,当把“D6 D0 CE C4”输出到IE中时,用“简体中文”方式查看,就能看到清楚的“中文”两个字了。(当然了,如果你一定要用“西欧字符”来看,那也没办法,你将得不到任何有何时何地的东西)为什么呢?因为“00D6 00D0 00CE 00C4”本来就是由ISO8859-1转化过去的。. T$ o* ]. ]2 e
. U( p9 v6 y! ^% E给出如下结论: 4 @* X$ P I3 z& B$ ?, X2 @ % H2 u3 _4 K! \% u+ Y* i 在Class输出字符串前,会将Unicode的字符串按照某一种内码重新生成字节流,然后把字节流输入,相当于进行了一步“String.getBytes(???)”操作。???代表某一种字符集。 + z% _5 H! G9 D" d% R ' a2 s ]) w; Q/ | m4 \7 s4 O4 s 如果是Servlet,那么,这种内码就是在HttpServletResponse.setContentType()方法中指定的内码,也就是上文定义的<Servlet-charset>。 ' w: t2 ?& X5 z4 N" P$ S! U0 Y z2 `9 S
如果是JSP,那么,这种内码就是在<%@ page contentType=""%>中指定的内码,也就是上文定义的<Jsp-charset>。$ ]" B4 A8 }$ H" M+ U8 Y
. A k7 y3 c. ]4 v' V' _, I5 k
如果是Java程序,那么,这种内码就是file.encoding中指定的内码,默认为ISO8859-1。 ' s( c- \3 k! G l9 l# D9 j; S3 j7 W : H# o9 X# O6 Q/ a5 d- C 当输出对象是浏览器时 1 @+ _( n8 |! ?# m: m. Y/ _ o T$ [! K# J7 A6 z% U/ B
以流行的浏览器IE为例。IE支持多种内码。假如IE接收到了一个字节流“D6 D0 CE C4”,你可以尝试用各种内码去查看。你会发现用“简体中文”时能得到正确的结果。因为“D6 D0 CE C4”本来就是简体中文中“中文”两个字的编码。 / [" o. `" n8 J P* ^: M7 _ : n$ i0 M! K, B8 H8 z OK,完整地看一遍。 . x9 b. u. W+ ~$ _. H: X2 ^( C, Q, F+ W1 K1 ~. P, h8 u
JSP:源文件为GB2312格式的文本文件,且JSP源文件中有“中文”这两个汉字 , _/ j: }# t* k @2 M# S& J9 p! S: j4 I( E2 W9 z% R% x
如果指定了<Jsp-charset>为GB2312,转化过程如下表。 . c% M2 d' B4 d$ ~1 { 6 ~( g& S/ d0 p* _8 P, l 表4 Jsp-charset = GB2312时的变化过程 * a" R2 Q" S; ?: H* z . E. I' ~8 h! g8 I序号步骤说明结果1编写JSP源文件,且存为GB2312格式D6 D0 CE C4- s2 G! |7 o7 k& O4 F
(D6D0=中 CEC4=文)2jspc把JSP源文件转化为临时JAVA文件,并把字符串按照GB2312映射到Unicode,并用UTF格式写入JAVA文件中E4 B8 AD E6 96 873把临时JAVA文件编译成CLASS文件E4 B8 AD E6 96 874运行时,先从CLASS文件中用readUTF读出字符串,在内存中的是Unicode编码4E 2D 65 87(在Unicode中4E2D=中 6587=文)5根据Jsp-charset=GB2312把Unicode转化为字节流D6 D0 CE C46把字节流输出到IE中,并设置IE的编码为GB2312(作者按:这个信息隐藏在HTTP头中)D6 D0 CE C47IE用“简体中文”查看结果“中文”(正确显示). j2 p8 Q0 q7 T% ]' b
如果指定了<Jsp-charset>为ISO8859-1,转化过程如下表。9 \9 t! O* [. V1 q/ C$ d& @" h