现在的位置: 首页 > 综合 > 正文

J2EE Web组件中中文及相关的问题(二)

2013年08月06日 ⁄ 综合 ⁄ 共 5654字 ⁄ 字号 评论关闭
 

 

3.        Java文件与编码

Java运用得如此广泛,以致于Java文件可能是采用任意一种字符编码的,如果不知道Java文件的编码标准是什么,就可能给我们的javac MyClass.java带来尴尬。所有文件的储存是都是字节的储存,在磁盘上保留的并不是文件的字

 

2-5 JVM输出

符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列,所以读取文件又可能涉及到字节解码到字符,如果javac在读取java文件时,没有采取正确的decoding,就如同我们用UTF-8GB2312字节流解码样,如果Java源程序中存在字符串常量(String str = “我是中国人”;),而这些常量又是非英文字符的话,那么javac将不能正确解码而形成乱码,甚至javacJava文件语法错误。由于java.exeoptions并没有关于encodingdecoding的选项,所以可以肯定在javac MyClass.java生成的class文件(byte code)中,字符串常量在编译Java源文件时,就被按照某种固定的编码标准编码在class文件中,按JVMJava Virtual Machine, Java虚拟机)的规范,这个固定的编码就是UTF-8,在class文件中使用CONSTANT_Utf8_info结构表示常量字符串值。图 2-6就是用Dos命令debug.exe查看一个class文件(debug.exe不直接支持扩展名大于三个ACSII字符的文件,将Show.class拷贝到Show.txt),用红线标记出的正是字符串我是中国人UTF-8编码结果。

2-6 debug查看class文件

让我们再来做一些实验吧。

public class Show

{

        static

        {

                 String str = "我是中国人";

                 System.out.println(str);

        }

        public static void main(String args[]){}

}

我使用的操作系统的default charsetGBK,所以我的Show.java存储时的编码也是GBK,而javacdefault decoding与系统所使用的decoding相同,也就是说我不必给javacoptions添加”-encoding GBK”javac也能对正确Show.java解码。可是如果我添加了”-encoding UTF-8”” -encoding ISO8859-1”将会怎样呢,你可能已经知道了一些结果,如图 7-2,也许你在那里面发现了眼熟的东西,但我要说的并不是和图 2-5里面相同的乱码,为什么不看看图 2-1呢,但请不要把两个乱码字符串进行比较,因为它们本来就是不正确的字符串,对本来就不正确的东西进行比较,很多时候都是没有意义的,你根本就不能确认它们是什么,即使它们看起来是显示相同的字符串。

2-7javac Show.java采取不同的解码

先不要着急,你可以先思考一下我说的东西,再进行下面的一些实验。

用记事本打开Show.java,然后用同名另存它,不过在另存时编码选择“UTF-8”(Win 2000及更高版本的Windows中的记事本,都可以这么做)。你已经知道了,我们现在该用javac –encoding UTF-8 Show.java命令行了,可是我们又遇到尴尬了,如图 2-8所示

2-8编译报错——我们又一次尴尬了

javac报出了我的语法错误,这可有些让人着实想不通了,找来找去都找不到错误出处,是的,你找不到,不管你是用记事本打开这个Show.java还是用MSInterDev中的VJ++都找不出错误所在,一切都好好的,报错所指的第一行中,根本就不存在那两个字符。还好一切都逃不过debug.exe的眼睛,原来Show.java文件的前三个字节是0xEF0xBB0xBF,虽然不懂它的意思,但还是可以理解的,因为在未打开文件前,Windows不知道它所要处理的文件是用什么编码的,这不像在Internet上,我们可以从信息的附加信息中知道信息所使用的编码,而Windows却不能(也许它应该这么做了),大概是为了标识该文件是用UTF-8编码的吧,系统只好在文件前加上了那三个字节(我们实在无法就凭着几个字节就能判断或确定出它是用什么编码编码的,XML中不也是要指定encoding的么)。可这下javac就不同意了(也许这种做法是MS自己想出来的吧),它仍将文件的前三个字节当作有效数据而不是特殊的标识,这下就错了。用debug把前面那三个字节删除(见图 2 –11)就行了,但当我们用GBKGB2312解码时,javac报语法错误了,这是应该的;用ISO8859-1虽通过了编译,却又出现了乱码,这也是应该的。如图2-9

2-9 Show. java是用UTF-8编码的

我们还可以得出一个结论的:如果Java文件中不存在非英文字符的字符串常量,我们是有理由不去关心javacoptions中的-encoding了,不管这个Java类在将来是否会去处理非英文字符,因为我们已经完全生成了正确的class,剩下的有关编码或decoding的事,为什么不交给它去做呢,说到这里,我们有必要再做一个实验。

4.        文件的编码

这个实验是用段Java小程序读取中文文件的,不过在这之前,你最好先看看这几个类的Java Document吧,java.io.FileInputStream, java.io.InputStreamReader, java.io.BufferedReader,我从Sun的《JavaTM 2 SDK, Standard Edition Documentation Version 1.4.0》中摘抄了一点很令我们振奋的Document

An InputStreamReader is a bridge from byte streams to character streams: It reads bytes and decodes them into characters using a specified charset. The charset that it uses may be specified by name or may be given explicitly, or the platform's default charset may be accepted.

 

Each invocation of one of an InputStreamReader's read() methods may cause one or more bytes to be read from the underlying byte-input stream. To enable the efficient conversion of bytes to characters, more bytes may be read ahead from the underlying stream than are necessary to satisfy the current read operation.

public int read()

         throws IOException

Read a single character.

通过InputStreamReader来读取文件或其他输入流,我们已经不直接读取到byte了,而我们所读取到的char也是InputStreamReader使用“自以为正确的解码”来解码字节流(type stream)所得的结果,因为如果我们在构造InputStreamReader对象时,没有使用参数charset来指定字节流的编码(编码字符流时所采用的encoding),它将使用缺省的编码(encoding)来作为解码(decoding)OutputStreamWriter有着相同的机制却执行着相反的动作。Utf_8File.java的源代码:

import java.io.FileInputStream;

import java.io.InputStreamReader;

import java.io.BufferedReader;

import java.io.File;

import java.io.IOException;

import java.io.FileNotFoundException;

import java.io.UnsupportedEncodingException;

 

public class Utf_8File

{

      public static void main(String args[])

             throws FileNotFoundException,IOException,UnsupportedEncodingException

      {           

             if(args.length < 3){

                    System.out.println("cmd encoding decoding file");

                    return;

             }

            

             FileInputStream fis = new FileInputStream(args[2]);

             InputStreamReader isr = new InputStreamReader(fis, args[0]);

             BufferedReader br = new BufferedReader(isr);

             String str;

             System.out.println("File content:");

             while((str = br.readLine()) != null)

                    System.out.println(str);

             br.close();

                          

             File f = new File(args[2]);

             fis = new FileInputStream(args[2]);

             byte bs[] = new byte[(int)f.length()];

             int b, index = 0;

             while((b = fis.read()) != -1)

                    bs[index++] = (byte)b;

             fis.close();

             System.out.println("File content:");

             System.out.print(new String(bs, args[1]));         

      }

}

用记事本编辑一个Show.txt文件,里面只有五个汉字:我是中国人,保存的时候用UTF-8编码,如图 2-10,请注意我的每一个命令行。

不错,第一次读取Show.txt时出现了乱码,都是文件前面那三个讨厌的流氓字节惹的祸,我们用debug把前面那三个字节删除,如图 2-11,第二次就好好的了。除此之外我们好像并没有发现乱码,但你也会发现“java Utf_8File utf-8 gb2312 Show.txt”的第二个“File content:”后什么也没有,我们用GB2312解码UTF-8编码的字节流是失败了(我并没有说对其他的UTF-8编码字节流用GB2312解码也不会得到什么字符)。

2-10 Java程序读取Show.txt

2-11 再见了,你们这些小流氓

你很细心,发现了图 2-10中的那个MalformedInputException,这是可以解释的,正是因为“java Utf_8File utf-8 gb2312 Show.txt”的第二个“File content:”后什么也没有,说过的,我们在这里没有得到任何字符,也许在其他地方可能会得到字符,但那是乱码(Malformed Character)

2-12 我们的确收到了这个Exception

现在,你也可以明白为什么System.outSystem.in可以乖乖地干活了吧,也可以知道我们以往不考虑这些东西好像也没出错的原因了吧,JVM用缺省的encoding/decoding帮我们做好了这些。那好,为什么不用GB2312编码另存Show.txt试试。

现在我们可以直面图 2-1所遇的尴尬了。

5.        JSP文件与编码

是的,我的JSP文件的编码是GB2312,如果我使用的Tomcat编译这个JSP文件没有使用GB2312作为decoding,又或是discomfiture$jsp被编译时,没有使用GB2312作为decoding,那么含非英文字符的常量字符串将不会被正确编译,也就会出现了乱码,因为我们可以肯定System.out是不会把正确有字符输出错误的。其实问题没有这么多,JSP引擎(JSP engine)在将JSP文件编译成Java文件后,javac编译这个Java文件时所用的encoding参数是UTF-8,也就是说JSP引擎所生成discomfiture$jsp.javap,而这个文件的存储时的编码(encoding)UTF-8就行了,而我们所在乎的就只是JSP引擎编译JSP文件所使用的编码。

先让JSP引擎正确编译discomfiture.jsp,我们得看看discomfiture$jsp.java

抱歉!评论已关闭.