首页 > 编程笔记

Java字符流(Reader和Writer)的用法

字符流是有别于字节流的另外一种数据流,两者的区别在于每次处理的数据单位不同。一个是以字节为单位,一个是以字符为单位,相当于两个尺寸的水瓢,每次舀水的量不同。

字符流也分为输入字符流(Reader)和输出字符流(Writer)。本节我们来学习字符流相关的知识。

Java Reader输入字符流类

Reader 是一个抽象类,实现了 Readable 和 Closeable 接口,定义如下:
public abstract class Reader implements Readable, Closeable{}
Readable 接口的作用是可以将数据以字符的形式读入缓冲区,Reader 常用方法如下表所示。

方 法 描 述
public int read() throws lOException 以字符为单位读数据
public int read(char cbuf[]) throws lOException 将数据读入 char 类型数组,并返回数据长度
public abstract int read(char cbuf[], int off, int len) throws lOException; 将数据读入 char 类型数组的指定区间,并返回数据长度
public abstract void close() throws lOException 关闭数据流
public long transferTo(Writer out) 将数据直接读入字符输出流

我们在使用 Reader 进行读入操作时,不能直接实例化 Reader 对象,应该实例化其实现了抽象方法的子类 FileReader,定义如下:
public class FileReader extends InputStreamReader{}
可以看到 FileReader 又继承自 InputStreamReader,这个类才是 Reader 的直接子类,它的作用是将字节流转为字符流,属于处理流。后面我们会详细讲解 InputStreamReader 类,这里我们直接使用 FileReader 进行实例化操作。

接下来我们学习 Reader 的常用方法,还是以操作 test.txt 文件为例,用 Reader 对其进行读取,代码如下:
public class Test {
   public static void main(String[] args) {
      try {
         Reader reader = new FileReader("/Users/southwind/Desktop/test.txt");
         int temp = 0;
         while((temp = reader.read())!=-1) {
            System.out.println(temp);
         }
         reader.close();
      } catch (FileNotFoundException e) {
      }
   }
}
运行结果为:

105
110
99

看到这里,你可能会有疑问了,为什么使用字符流读取出来的结果和使用字节流是一样的?这是因为 test.txt 中保存的数据是“ioc”,在 UTF-8 编码规范下,1 个英文字符占用 1 字节的空间,即这里每读取 1 个字符就等于读取了 1 字节。所以两种流的结果是一样的,Java 工程默认的编码方式为 UTF-8,如下图所示。


图 1 默认的编码方式为 UTF-8

但是汉字就完全不同了,在 UTF-8 编码规范下,1 个汉字占用 3 字节内存。我们也可以将 test.txt 的内容修改为“你好”,使用字符流和字节流同时读取 test.txt,代码如下:
public class Test {
   public static void main(String[] args) {
      try {
         System.out.println("用字符流读取数据");
         Reader reader = new FileReader("/Users/southwind/Desktop/test.txt");
         int temp = 0;
         while((temp = reader.read())!=-1) {
            System.out.println(temp);
         }
         reader.close();
         System.out.println("用字节流读取数据");
         InputStream inputStream = new FileInputStream("/Users/southwind/Desktop/ test.txt");
         int temp2 = 0;
         while((temp2 = inputStream.read())!=-1) {
            System.out.println(temp2);
         }
         inputStream.close();
      } catch (FileNotFoundException e) {
      }
   }
}
运行结果为:

用字符流读取数据
20320
22909
用字节流读取数据
228
189
160
229
165
189

通过结果可以看到,如果是汉字,那么字符流和字节流读取的结果就完全不同了。“你好”这两个汉字用字符流读取出来是两个字符数据,20320 表示“你”,22909 表示“好”。如果使用字节流,1 个汉字对应 3 字节,所以“228,189,160”这 3 个字节组合起来表示“你”,“229,165,189”这 3 个字节组合起来表示“好”。

通过这个例子,相信大家就可以把字节流和字符流的区别搞清楚了,Reader 其他常用方法的使用可以参考下方的代码:
public class Test {
   public static void main(String[] args) {
      try {
         Reader reader = new FileReader("/Users/southwind/Desktop/test.txt");
         char[] chars = new char[8];
         int length = reader.read(chars);
         System.out.println("数据流长度:"+length);
         System.out.println("遍历char数组");
         for (char c : chars) {
            System.out.println(c);
         }
         reader.close();
         System.out.println("*****************************");
         reader = new FileReader("/Users/southwind/Desktop/test.txt");
         chars = new char[8];
         length = reader.read(chars,2,6);
         System.out.println("数据流长度:"+length);
         System.out.println("遍历char数组");
         for (char c : chars) {
            System.out.println(c);
         }
         reader.close();
      } catch (FileNotFoundException e) {
      }
   }
}

Java Writer字符输出流类

接下来我们学习字符输出流 Writer,定义为:
public abstract class Writer implements Appendable, Closeable, Flushable{}
Appendable 接口可以将 char 类型的数据读入数据缓冲区,Writer 的常用方法如下表所示。

方 法 描 述
public void write(int c) throws lOException 以字符为单位写数据
public void write(char cbuf[]) throws lOException 将 char 类型数组中的数据写出
public abstract void write(char cbuf[], int off, int len) throws lOException 将 char 类型数组中指定区间的数据写出
public void write(String str) throws lOException 将 String 类型的数据写出
public void write(String str, int off, int len) throws lOException 将 String 类型指定区间的数据写出
public abstract void flush() throws lOException 可以强制将缓冲区的数据同步到输出流中
public abstract void close() throws lOException 关闭数据流

我们在使用 Writer 进行写出操作时,不能直接实例化 Writer 对象,应该实例化其实现了抽象方法的子类 FileWriter,定义为:
public class FileWriter extends OutputStreamWriter{}
FileWriter 又继承自 OutputStreamWriter,和 InputStreamReader 一样,OutputStreamWriter 也属于处理流,在后面的章节我们会详细讲解。这里我们直接使用 FileWriter 进行实例化操作,接下来我们学习 Writer 的常用方法如何使用,比如 writer(int c) 的使用可以参考下面的程序:
public class Test {
   public static void main(String[] args) {
      try {
         Writer writer = new FileWriter("/Users/southwind/Desktop/test2.txt");
         writer.write(20320);
         writer.write(22909);
         writer.flush();
         writer.close();
      } catch (IOException e) {
      }
   }
}
代码实现了在桌面创建文件 test2.txt,并且向文件中写入 byte 类型的数据 20320 和 22909,byte 类型的数据会转换成对应的字符,20320 对应的是’你’,22909 对应的是‘好’,运行结果是我们会看到程序在桌面创建了 test2.txt,打开test2.txt,可以看到内容为你好。

推荐阅读

副业交流群 关注微信公众号,加入副业交流群,学习变现经验,交流各种打法。