前段时间写的深入浅出Java中文问题系列描述了很多Java各种应用中出现的中文问题,唯独没有说到文件的读写。最近用Java处理文件的时候,同样遇到了中文问题,觉得还是有必要总结一下,也使该系列的文章更加完整。
    熟悉Java 的人都知道,在Java中,IO是分成两大部分的,分别对应字节和字符的操作,也就是Stream和Character,它们之间可以相互转换,桥梁就是StreamInputReader/StreamOutputWriter。为了更加清楚的了解它们之间的关系,我们可以看看它们所在的类结构。
  • java.lang.Object
    • java.io.InputStream (implements java.io.Closeable)
    • java.io.OutputStream (implements java.io.Closeable, java.io.Flushable)
    • java.io.RandomAccessFile (implements java.io.Closeable, java.io.DataInput, java.io.DataOutput)
    • java.io.Reader (implements java.io.Closeable, java.lang.Readable)
      • java.io.BufferedReader
      • java.io.InputStreamReader
        • java.io.FileReade
    • java.io.Writer (implements java.lang.Appendable, java.io.Closeable, java.io.Flushable)
      • java.io.BufferedWriter
      • java.io.OutputStreamWriter
        • java.io.FileWriter

    上面列出来的并不是Java.io中全部的类,但是对于文件读写来说已经足够了。通常,我们使用以下代码来进行文件的读写:

    public void naiveWrite() throws IOException{
      FileWriter fw = new FileWriter("test.txt");
      fw.write("中文你好");
      fw.close();
    }

    public String naiveRead() throws IOException{
      FileReader fr = new FileReader("test.txt");
      BufferedReader br = new BufferedReader(fr);
      String str = br.readLine();
      br.close();
      fr.close();
      return str;
    }

       如果我们的是中文平台,上面代码是可以正常运行的。但是如果我们把这些代码放到一个ISO8859-1的系统上,中文问题就出来了(当然,前提你在javac的时指定了编码方式,如javac -encoding gb2312 ***.java,参看该系列前面的文章)。为什么呢?这是因为FileWriter和FileReader是辅助类,为了方便大家使用 OutputSteamWriterer 和 InputStreamReader 而屏蔽了字符集的设定操作,而采用系统默认的编码方式,而这在很多情况下也能满足用户的需求。在中文系统中,系统的默认编码方式一般是GBK,因此文件中中文的读写是没有问题的。但是,当程序运行在ISO8859-1的系统中时,JVM使用ISO8859-1对中文进行编码,当然就认不到了,于是那一个个的问号就来了。
        那怎么办呢?既然捷径走不通,我们就只好使用OutputSteamWriter 和 InputStreamReader了。

    public void write() throws IOException{
      OutputStreamWriter osw = new OutputStreamWriter(
                           new  FileOutputStream("test.txt"), "utf-8");
      osw.write("中国万岁");
      osw.close();
    }
    public String read() throws IOException{
      InputStreamReader isr = new InputStreamReader(
            new FileInputStream("test.txt"),"utf-8");
      BufferedReader br = new BufferedReader(isr);
      String str = br.readLine();
      br.close();
      isr.close();
      return str;
    }
      
         在这里,我们指定文件读写的编码方式为utf-8,当然对于中文来说GBK和GB2312也是可以的,但是推荐使用UTF-8,这样对于软件的国际化很有好处。其实,这里指定编码方式进行文件的写入跟我们使用记事本等编辑器的另存为,并且指定格式为“UTF-8”在本质上是一样的。通过上述处理后,程序就可以跨平台运行了。
        在处理文件的过程中,我们还会用到RandomAccessFile这个类来随机访问文件。这里,如果我们写入字符串的时候调用writeChars,那么,如果写入的是中文,中文问题就又会出现了。因为此时RandomAccessFile并没有使用系统的默认编码来写入文件,而是直接将内存中的二进制数据直接写到文件中去。如何解决这个问题呢?只要读写对称就行了。
    public void randWrite() throws IOException{
      RandomAccessFile raf = new RandomAccessFile("test1.txt","rw");
      raf.writeChars("中国你好");
      raf.close();
    }
    public String randRead() throws IOException{
      RandomAccessFile raf = new RandomAccessFile("test1.txt","r");
      StringBuffer sb = new StringBuffer();
      while( raf.getFilePointer() < raf.length()){
       sb.append( raf.readChar() );
      }
       raf.close();
      return sb.toString();
    }  
    但是这样处理起来不是很方便,我们可以这样写:
    public void randWrite() throws IOException{
      RandomAccessFile raf = new RandomAccessFile("test1.txt","rw");
      raf.writeUTF("中国你好");
      raf.close();
    }
    public String randRead() throws Exception{
      RandomAccessFile raf = new RandomAccessFile("test1.txt","r");
      String str = raf.readUTF();
      raf.close();
      return str;
    }
      
       好了,文件读写的中文问题就解决了。

  • 文章来源:http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!405.entry