字符流操作文件
在程序开发中,经常需要对文本文件的内容进行读取,如果想从文件中直接读取字符便可以使用字符输入流FileReader,通过此流可以从文件中读取一个或一组字符。接下来通过一个案例来学习如何使用FileReader以字符的形式读取文件中的内容。
首先在项目当前目录下新建文本文件“reader.txt”并在其中输入字符“好好学习,天天向上!”,然后创建一个使用字符输入流FileReader读取文件中字符的类,如文件1所示。
文件1 Example07.java
1 import java.io.*;
2 public class Example07 {
3 public static void main(String[] args) throws Exception {
4 // 创建FileReader对象,并指定需要读取的文件
5 FileReader fileReader = new FileReader("reader.txt");
6 // 定义一个int类型的变量len,其初始化值为0
7 int len = 0;
8 // 通过循环来判断是否读取到了文件末尾
9 while ((len = fileReader.read()) != -1) {
10 // 输出读取到的字符
11 System.out.print((char)len);
12 }
13 // 关闭流
14 fileReader.close();
15 }
16 }
运行结果如图1所示。
图1 运行结果
文件1实现了读取文件字符的功能。首先创建一个FileReader对象来读取指定文件内容,然后通过while循环每次从文件中读取一个字符并打印,这样便实现了FileReader读文件字符的操作。由于字符输入流的read()方法返回的是int类型的值,如果想获得字符就需要进行强制类型转换,所以输出语句中将变量len强转为了char类型。
文件1讲解了如何使用FileReader读取文件中的字符,如果要向文件中写入字符就需要使用FileWriter类,该类是Writer的一个子类。接下来通过一个案例来学习如何使用FileWriter将字符写入文件,如文件2所示。
文件2 Example08.java
1 import java.io.*;
2 public class Example08 {
3 public static void main(String[] args) throws Exception {
4 // 创建字符输出流对象,并指定输出文件
5 FileWriter fileWriter = new FileWriter("writer.txt");
6 // 将定义的字符写入文件
7 fileWriter.write("轻轻的我走了,\r\n");
8 fileWriter.write("正如我轻轻的来;\r\n");
9 fileWriter.write("我轻轻的招手,\r\n");
10 fileWriter.write("作别西天的云彩。\r\n");
11 // 关闭流
12 fileWriter.close();
13 }
14 }
程序运行结束后,会在当前目录下生成一个名称为“writer.txt”的文件,打开此文件会看到如图2所示的内容。
图2 writer.txt
FileWriter同FileOutputStream一样,如果指定的文件不存在,就会先创建文件,再写入数据,如果文件存在,则会首先清空文件中的内容,再进行写入。如果想在文件末尾追加数据,同样需要调用重载的构造方法,现将文件2中的第5行代码进行如下修改:
FileWriter writer = new FileWriter("writer.txt",true);
修改后,再次运行程序,即可实现在文件中追加内容的效果。
上面两个文件示例通过字符流的形式完成了对文件内容的读写操作,但也是逐个字符进行读写,这样也需要频繁的操作文件,效率仍非常低。这里也可以使用提供的字符流缓冲区(类似于字节流缓冲区)进行读写操作,来提高执行效率。
接下来,通过一个案例来学习如何使用字符流的缓冲区实现文件的拷贝,如文件3所示。
文件3 Example09.java
1 import java.io.*;
2 public class Example09 {
3 public static void main(String[] args) throws Exception {
4 // 创建FileReader对象,并指定需要读取的文件
5 FileReader fileReader = new FileReader("reader.txt");
6 // 创建FileWriter对象,并指定写入数据的目标文件
7 FileWriter fileWriter = new FileWriter("writer.txt");
8 // 定义一个int类型的变量len,其初始化值为0
9 int len = 0;
10 // 定义一个长度为1024的字符数组
11 char[] buff = new char[1024];
12 // 通过循环来判断是否读取到了文件末尾
13 while ((len = fileReader.read(buff)) != -1) {
14 // 输出读取到的字符
15 fileWriter.write(buff, 0, len);
16 }
17 // 关闭流
18 fileReader.close();
19 fileWriter.close();
20 }
21 }
文件3使用字符流缓冲区实现了文本文件reader.txt的拷贝,具体操作与字节流缓冲区实现文件拷贝类似,只是创建的缓冲区不一样,这里是使用了new char[1024]创建了一个字符缓冲区。
同样,在字节流中提供了带缓冲区功能的字节缓冲流,字符流也提供了带缓冲区的字符缓冲流,分别是BufferedReader和BufferedWriter,其中BufferedReader用于对字符输入流进行操作,BufferedWriter用于对字符输出流进行操作。需要注意的是,在BufferedReader中有一个重要的方法readLine(),该方法用于一次读取一行文本。
接下来通过一个案例来学习如何使用这两个缓冲流实现文件的拷贝,如文件4所示。
文件4 Example10.java
1 import java.io.*;
2 public class Example10 {
3 public static void main(String[] args) throws Exception {
4 // 创建一个字符输入缓冲流对象
5 BufferedReader br = new BufferedReader(
6 new FileReader("reader.txt "));
7 // 创建一个字符输出缓冲流对象
8 BufferedWriter bw = new BufferedWriter(
9 new FileWriter("writer.txt"));
10 // 声明一个字符串变量str
11 String str = null;
12 // 循环时每次读取一行文本,如果不为null(即到了文件末尾),则继续循环
13 while ((str = br.readLine()) != null) {
14 // 通过缓冲流对象写入文件
15 bw.write(str);
16 // 写入一个换行符,该方法会根据不同的操作系统生成相应的换行符
17 bw.newLine();
18 }
19 // 关闭流
20 br.close();
21 bw.close();
22 }
23 }
程序运行结束后,同样会将源文件“reader.txt”中的内容拷贝到目标文件“writer.txt”中,结果如图3所示。
图3 拷贝后文件的内容
在文件4中,使用了输入输出字符缓冲流BufferedReader和BufferedWriter,并通过一个while循环实现了文本文件的拷贝。在拷贝过程中,每次循环都使用readLine()方法读取文件的一行,然后通过write()方法写入目标文件,同时使用了newLine()进行换行写入,否则读取源文件所有行内容都会追加写入目标文件一行中。其中readLine()方法会逐个读取字符,当读到回车符“\r”或换行符“\n”时会将读到的字符作为一行的内容返回。
需要注意的是,在字符缓冲流内部也使用了缓冲区,在循环中调用BufferedWriter的write()方法写入字符时,这些字符首先会被写入缓冲区,当缓冲区写满时或调用close()方法时,缓冲区中的字符才会被写入目标文件。因此在循环结束时一定要调用close()方法,否则极有可能会导致部分存在缓冲区中的数据没有被写入目标文件。