在 Java 的 I/O 体系中,FileReader 类(java.io.FileReader)的主要功能是把文件内容以字符流的形式进行读取。
FileReader 的工作原理和 FileInputStream 颇为相似,不过二者存在明显差异:FileInputStream 读取的是字节,而 FileReader 专注于读取字符。简单来讲,FileReader 就是专门为读取文本而设计的。要知道,一个字符可能对应一个或多个字节,这取决于具体的字符编码方案。
注意:FileReader 类继承自 Java 的 Reader 类,所以它拥有诸多和 Reader 类相同的方法。
以下是一个简单的 FileReader 示例:
package com.hxstrive.java_io; import java.io.FileReader; import java.io.IOException; import java.io.Reader; public class FileReaderExample { public static void main(String[] args) throws IOException { try(Reader reader = new FileReader("data.txt")) { int theCharNum = reader.read(); while (theCharNum != -1) { char theChar = (char) theCharNum; System.out.print(theChar); theCharNum = reader.read(); } } //输出: //你好!欢迎光临 } }
上述示例,首先创建了一个 FileReader,它直接连接到 FileReader 构造函数参数传递的文件路径指向的文件。其次,此示例从 FileReader 一次读取一个字符。最后,利用 Java7 的 try-with-resources 来自动关闭 FileReader。
FileReader 的 read() 方法返回一个 int,其中包含读取字符的字符值。如果 read() 方法返回 -1,则表示 FileReader 中已无数据可读,则可以关闭了。例如:
try(Reader reader = new FileReader("data.txt")) { int theCharNum = reader.read(); // 读取数据 while (theCharNum != -1) { char theChar = (char) theCharNum; System.out.print(theChar); theCharNum = reader.read(); // 读取数据 } }
上述示例仅从 FileReader 中读取字符,并未对这些字符做任何处理。
FileReader 也有一种方法可以读取字符数组,而不是一次读取一个字符。下面是一个将多个字符读入字符数组的示例:
FileReader fileReader = new FileReader("input.txt"); char[] destination = new char[1024]; // 字符数组 int charsRead = fileReader.read(destination, 0, destination.length);
上述示例中,read(char[]) 方法的第一个参数是要读入字符的字符数组,第二个参数是要写入字符的数组偏移量,第三个参数是要写入字符的最大长度。read(char[]) 方法返回实际读入字符数组的字符数。如果文件中的字符数少于您指定的最大字符数,那么读取的字符数将少于最大字符数。否则,read(char[]) 方法将尝试向数组中读取最大字符数。
注意:相较于通过 FileReader 一次读取一个字符,一次读取一个字符数组的速度要快得多。通过读取字符数组而非单个字符,性能轻松就能提升 10 倍甚至更多。
具体的速度提升幅度,取决于读取字符数组的大小,以及运行代码的计算机的操作系统、硬件等因素。在做决策前,你可以研究一下目标系统的硬盘缓冲区大小等参数。一般来说,8KB 及以上的缓冲区大小能带来不错的速度提升。不过,一旦字符数组大小超出了底层操作系统和硬件的承载能力,继续增大字符数组就无法再提高读取速度了。
你可以尝试使用不同大小的字节数组,并测量读取性能,从而找出最优的字符数组大小。
借助 BufferedReader 类,能够为 FileReader 增添透明的缓冲功能。BufferedReader 会从底层 FileReader 批量读取大量字符,存入字符数组。随后,你可以逐个读取 BufferedReader 中的字符,这种方式同样能显著提升读取效率,充分发挥批量读取字符数组相较于逐个读取字符的速度优势。以下是使用 BufferedReader 封装 FileReader 的示例:
int bufferSize = 1024 * 8; Reader input = new BufferedReader(new FileReader("input.txt"), bufferSize);
注意,BufferedReader 是 Reader 的子类,可在任何能使用 Reader 的地方使用。
FileReader 会默认采用应用程序运行所在计算机的默认字符编码,对文件中的字节进行解码。但这或许并非你所期望的,而且你还无法更改这一默认设置!
若你想指定其他字符解码方案,就别使用 FileReader 了,可在 FileInputStream 上搭配使用 InputStreamReader。借助 InputStreamReader,你能够自行指定在读取底层文件字节时所采用的字符编码方案。
当从 FileReader 完成字符读取操作后,务必记得将其关闭。要关闭 FileReader,只需调用它的 close() 方法即可。以下为你展示关闭 Java FileReader 的具体步骤:
fileReader.close();
当然,你还可以使用 Java7 中引入的 try-with-resources 结构,让它自动帮你关闭资源,如下:
try(FileReader fileReader = new FileReader("text.txt")){ int data = fileReader.read(); while(data != -1) { System.out.print((char) data)); data = fileReader.read(); } }
注意,上面示例中没有手动调用 close() 方法,而是让 try-with-resources 自动去调用 close() 方法。