9.1  概述

根据 Sun 的文档,一个 Charset 是“十六位 Unicode 字符序列与字节序列之间的一个命名的映射”。实际上,一个 Charset 允许您以尽可能最具可移植性的方式读写字符序列。

Java 语言被定义为基于 Unicode。然而在实际上,许多人编写代码时都假设一个字符在磁盘上或者在网络流中用一个字节表示。这种假设在许多情况下成立,但是并不是在所有情况下都成立,而且随着计算机变得对 Unicode 越来越友好,这个假设就日益变得不能成立了。

在本节中,我们将看一下如何使用 Charsets 以适合现代文本格式的方式处理文本数据。这里将使用的示例程序相当简单,不过,它触及了使用 Charset 的所有关键方面:为给定的字符编码创建 Charset,以及使用该 Charset 解码和编码文本数据。

9.2  编码/解码

要读和写文本,我们要分别使用CharsetDecoder CharsetEncoder。将它们称为编码器解码器是有道理的。一个字符不再表示一个特定的位模式,而是表示字符系统中的一个实体。因此,由某个实际的位模式表示的字符必须以某种特定的编码来表示。

CharsetDecoder 用于将逐位表示的一串字符转换为具体的 char 值。同样,一个 CharsetEncoder 用于将字符转换回位。

在下一个小节中,我们将考察一个使用这些对象来读写数据的程序。

9.3  处理文本的正确方式

现在我们将分析这个例子程序UseCharsets.java。这个程序非常简单——它从一个文件中读取一些文本,并将该文本写入另一个文件。但是它把该数据当作文本数据,并使用 CharBuffer 来将该数句读入一个 CharsetDecoder 中。同样,它使用 CharsetEncoder 来写回该数据。

我们将假设字符以ISO-8859-1(Latin1) 字符集(这是 ASCII 的标准扩展)的形式储存在磁盘上。尽管我们必须为使用 Unicode 做好准备,但是也必须认识到不同的文件是以不同的格式储存的,而 ASCII 无疑是非常普遍的一种格式。事实上,每种 Java 实现都要求对以下字符编码提供完全的支持:

US-ASCII

ISO-8859-1

UTF-8

UTF-16BE

UTF-16LE

UTF-16



9.4  示例程序

在打开相应的文件、将输入数据读入名为 inputData  ByteBuffer 之后,我们的程序必须创建 ISO-8859-1 (Latin1) 字符集的一个实例:

1
Charset latin1 = Charset.forName(  "ISO-8859-1"  );

然后,创建一个解码器(用于读取)和一个编码器(用于写入):

1
2
CharsetDecoder decoder = latin1.newDecoder();
CharsetEncoder encoder = latin1.newEncoder();

为了将字节数据解码为一组字符,我们把ByteBuffer 传递给 CharsetDecoder,结果得到一个 CharBuffer

1
CharBuffer cb = decoder.decode( inputData );

如果想要处理字符,我们可以在程序的此处进行。但是我们只想无改变地将它写回,所以没有什么要做的。

要写回数据,我们必须使用 CharsetEncoder 将它转换回字节:

1
ByteBuffer outputData = encoder.encode( cb );

在转换完成之后,我们就可以将数据写到文件中了。



示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// UseCharsets
import  java.io.*;
import  java.nio.*;
import  java.nio.channels.*;
import  java.nio.charset.*;
public  class  UseCharsets
{
   static  public  void  main( String args[] )  throws  Exception {
     String inputFile =  "samplein.txt" ;
     String outputFile =  "sampleout.txt" ;
     RandomAccessFile inf =  new  RandomAccessFile( inputFile,  "r"  );
     RandomAccessFile outf =  new  RandomAccessFile( outputFile,  "rw"  );
     long  inputLength =  new  File( inputFile ).length();
     FileChannel inc = inf.getChannel();
     FileChannel outc = outf.getChannel();
     MappedByteBuffer inputData =
       inc.map( FileChannel.MapMode.READ_ONLY,  0 , inputLength );
     Charset latin1 = Charset.forName(  "ISO-8859-1"  );
     CharsetDecoder decoder = latin1.newDecoder();
     CharsetEncoder encoder = latin1.newEncoder();
     CharBuffer cb = decoder.decode( inputData );
     // Process char data here
     ByteBuffer outputData = encoder.encode( cb );
     outc.write( outputData );
     inf.close();
     outf.close();
   }
}



中文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import  java.io.File;
import  java.io.FileInputStream;
import  java.io.IOException;
import  java.nio.ByteBuffer;
import  java.nio.CharBuffer;
import  java.nio.channels.FileChannel;
import  java.nio.charset.Charset;
import  java.nio.charset.CharsetDecoder;
public  class  ReadByteBuffer {
     public  static  void  main(String[] args)  throws  IOException {
         /**
          * 第一步是获取通道。我们从 FileInputStream 获取通道:
          * */
         //文件为UTF-8
         File file =  new  File( "C:\\Users\\Qiang\\Desktop\\hydra.txt" );
         FileInputStream fStream =  new  FileInputStream(file);
         FileChannel fChannel = fStream.getChannel();
         ByteBuffer byteBuffer = ByteBuffer.allocate(( int ) file.length());
         CharBuffer charBuffer=CharBuffer.allocate(( int ) file.length());
         //获取当前系统的编码格式
         String encoding = System.getProperty( "file.encoding" );
         // 创建UTF-8字符集
         Charset charset = Charset.forName(encoding);
         //通过构造CharsetEncoder和CharsetDecoder将字符序列转换成字节和逆转换。
         CharsetDecoder decoder = charset.newDecoder();
         try  {
             int  len=fChannel.read(byteBuffer);
             while  (len!=- 1 ) {
                 byteBuffer.flip();
                 decoder.decode(byteBuffer, charBuffer,  false );
                 charBuffer.flip();
                 System.out.println(charBuffer);
                 byteBuffer.clear();
                 charBuffer.clear();
                 len=fChannel.read(byteBuffer);
             }
          
         catch  (Exception e) {
             // TODO: handle exception
         } finally {
             fChannel.close();
             fStream.close();
         }
              
     }
     private  static  byte [] getBytes( char [] chars) { // 将字符转为字节(编码)
         Charset cs = Charset.forName( "UTF-8" );
         CharBuffer cb = CharBuffer.allocate(chars.length);
         cb.put(chars);
         cb.flip();
         ByteBuffer bb = cs.encode(cb);
         return  bb.array();
     }
     private  static  char [] getChars( byte [] bytes) { // 将字节转为字符(解码)
         Charset cs = Charset.forName( "UTF-8" );
         ByteBuffer bb = ByteBuffer.allocate(bytes.length);
         bb.put(bytes);
         bb.flip();
         CharBuffer cb = cs.decode(bb);
         return  cb.array();
     }
}