目录
1.字符流定义及基本用法
前面已经讲解过InputStream类和OutputStream类在读写文件时操作的都是字节,如果希望在程序中操作字符,使用这两个类就不太方便,为此JDK提供了字符流。同字节流一样,字符流也有两个抽象的顶级父类,分别是Reader和Writer。其中Reader是字符输入流,用于从某个源设备读取字符。Writer是字符输出流,用于向某个目标设备写入字符。
Reader和Writer作为字符流的顶级父类,也有许多子类,接下来通过一张继承关系图列举Reader和Writer的一些常用子类。
2.缓冲字符流
字符流的继承关系与字节流的继承关系有些类似,很多子类都是成对(输入流和输出流)出现的,其中FileReader和FileWriter用于读写文件,BufferedReader和BufferedWriter是具有缓冲功能的流,使用它们可以提高读写效率。
在程序开发中,经常需要对文本文件的内容进行读取,如果想从文件中直接读取字符便可以使用字符输入流FileReader,通过此流可以从关联的文件中读取一个或一组字符。接下来通过一个案例来学习如何使用FileReader读取文件中的字符。
首先在Java项目的根目录下新建文本文件“reader.txt”并在其中输入字符“itcast”,然后创建一个类Example15,在类Example15中创建字符输入流FileReader对象读取文件中的内容。
import java.io.*;
public class Example15 {
public static void main(String[] args) throws Exception {
// 创建一个FileReader对象用来读取文件中的字符
FileReader reader = new FileReader("reader.txt");
int ch; // 定义一个变量用于记录读取的字符
while ((ch = reader.read()) != -1) { // 循环判断是否读取到文件的末尾
System.out.println((char) ch); // 不是字符流末尾就转为字符打印
}
reader.close(); // 关闭文件读取流,释放资源
}
}
上述代码中,第5行代码创建一个FileReader对象与文件关联,第7~9行代码通过while循环每次从文件中读取一个字符并打印,这样便实现了FileReader读文件字符的操作。需要注意的是,字符输入流的read()方法返回的是int类型的值,如果想获得字符就需要进行强制类型转换,如程序中第8行代码就是将变量ch转为char类型再打印。
在上述程序中,FileReader对象返回的字符流是char而InputStream对象返回的字符流是byte这就是两者之间最大的区别。下面我们讲解字符流怎么写入字符,如果要向文件中写入字符就需要使用FileWriter类,该类是Writer的一个子类。
2.1 使用FileWriter将字符写入文件
接下来通过一个案例来学习如何使用FileWriter将字符写入文件。
import java.io.*;
public class Example16 {
public static void main(String[] args) throws Exception {
// 创建一个FileWriter对象用于向文件中写入数据
FileWriter writer = new FileWriter("writer.txt");
String str = "你好,你的名字";
writer.write(str); // 将字符数据写入到文本文件中
writer.write("\r\n"); // 将输出语句换行
writer.close(); // 关闭写入流,释放资源
}
}
程序运行结束后,会在当前目录下生成一个名称为“writer.txt”的文件,打开此文件会看到如下内容。
FileWriter同FileOutputStream一样,如果指定的文件不存在,就会先创建文件,再写入数据,如果文件存在,则会首先清空文件中的内容,再进行写入。如果想在文件末尾追加数据,同样需要调用重载的构造方法,现将程序中的第5行代码进行如下修改:
FileWriter writer = new FileWriter("writer.txt",true);
再次运行程序,即可实现在文件中追加内容的效果。
包装流可以对一个已存在的流进行包装来实现数据读写功能,利用包装流可以有效地提高读写数据的效率。字符流同样提供了带缓冲区的包装流,分别是BufferedReader和BufferedWriter,其中BufferedReader用于对字符输入流进行包装,BufferedWriter用于对字符输出流进行包装。需要注意的是,在BufferedReader中有一个重要的方法readLine(),该方法用于一次读取一行文本。
2.2 包装流实现文件的拷贝
接下来通过一个案例来学习如何使用这两个包装流实现文件的拷贝,首先我们在根目录下新建文件“src.txt”,然后编写代码实现文件拷贝。
import java.io.*;
public class Example17 {
public static void main(String[] args) throws Exception {
FileReader reader = new FileReader("src.txt");
// 创建一个BufferedReader缓冲对象
BufferedReader br = new BufferedReader(reader);
FileWriter writer = new FileWriter("des.txt");
// 创建一个BufferdWriter缓冲区对象
BufferedWriter bw = new BufferedWriter(writer);
String str;
// 每次读取一行文本,判断是否到文件末尾
while ((str = br.readLine()) != null) {
bw.write(str);
// 写入一个换行符,该方法会根据不同的操作系统生成相应的换行符
bw.newLine();
}
br.close();
bw.close();
}
}
程序运行结束后,打开当前目录下的文件“src.txt”和“des.txt”,内容如下图。
上述代码中,第6~9行代码分别使用了输入输出流缓冲区对象,第12~16行通过一个while循环实现了文本文件的拷贝。在拷贝过程中,每次循环都使用readLine()方法读取文件的一行,然后通过write()方法写入目标文件。其中readLine()方法会逐个读取字符,当读到回车符'\r'或换行符'\n'时会将读到的字符作为一行的内容返回。
由于字符缓冲流内部使用了缓冲区,在循环中调用BufferedWriter的write()方法写入字符时,这些字符首先会被写入缓冲区,当缓冲区写满时或调用close()方法时,缓冲区中的字符才会被写入目标文件。因此在循环结束时一定要调用close()方法,否则极有可能会导致部分存在缓冲区中的数据没有被写入目标文件。
3.转换流
前面提到IO流可分为字节流和字符流,有时字节流和字符流之间也需要进行转换。在JDK中提供了两个类可以将字节流转换为字符流,它们分别是InputStreamReader和OutputStreamWriter。
InputStreamReader是Reader的子类,它可以将一个字节输入流转换成字符输入流,方便直接读取字符。OutputStreamWriter是Writer的子类,它可以将一个字节输出流转换成字符输出流,方便直接写入字符。通过转换流进行数据读写的过程如下图。
通过转换流进行数据读写的过程接下来通过一个案例来学习如何将字节流转为字符流,为了提高读写效率,可以通过BufferedReader和BufferedWriter实现转换工作。
import java.io.*;
public class Example18 {
public static void main(String[] args) throws Exception {
// 创建字节输入流
FileInputStream in = new FileInputStream("src.txt");
// 将字节流输入转换成字符输入流
InputStreamReader isr = new InputStreamReader(in);
// 赋予字符流对象缓冲区
BufferedReader br = new BufferedReader(isr);
FileOutputStream out = new FileOutputStream("des.txt");
// 将字节输出流转换成字符输出流
OutputStreamWriter osw = new OutputStreamWriter(out);
// 赋予字符输出流对象缓冲区
BufferedWriter bw = new BufferedWriter(osw);
String line;
while ((line = br.readLine()) != null) { // 判断是否读到文件末尾
bw.write(line); // 输出读取到的文件
}
br.close();
bw.close();
}
}
在src.txt文件中输入内容“你好,itcast。”,程序运行结束后,文件“src.txt”和“des.txt”文件中的内容如下图。
上述代码实现了字节流和字符流之间的转换,将字节流转换为字符流,从而实现直接对字符的读写。需要注意的是,在使用转换流时,只能针对操作文本文件的字节流进行转换,如果字节流操作的是一张图片,此时转换为字符流就会造成数据丢失。