BufferedReader和FileReader之间的具体区别是什么?

69
我想知道BufferedReaderFileReader之间的具体区别。
我知道BufferedReaderFileReader更高效,但有人能详细解释一下为什么吗?
6个回答

149

首先,你应该了解Java中的“流”概念,因为Java中的所有“Readers”都是基于这个概念构建的。

文件流

Java中的文件流由FileInputStream对象执行。

// it reads a byte at a time and stores into the 'byt' variable
int byt;
while((byt = fileInputStream.read()) != -1) {
    fileOutputStream.write(byt);
} 

这个对象每次读取一个字节(8位),并将其写入给定的文件。

它的一个实用的应用是处理原始的二进制/数据文件,例如图像或音频文件(对于音频文件使用AudioInputStream而不是FileInputStream)。 另一方面,对于文本文件来说,由于循环每次一个字节,然后进行一些处理并将处理过的字节存储回去是繁琐且耗时的,因此非常不方便和慢。

您还需要提供文本文件的字符集,即如果字符为拉丁字符或中文字符等。否则,程序将每次解码和编码8位,并在屏幕上或输出文件中看到奇怪的字符(如果字符超过1个字节长度,即非ASCII字符)。

文件读取

这只是一种高级的方式,可以支持包含字符集的“文件流”(即无需像之前那样定义字符集)。

FileReader类专门设计用于处理文本文件。 正如您之前所见,文件流最适合处理原始二进制数据,但为了处理文本,它并不太有效。

因此,Java-dudes添加了FileReader类,专门处理文本文件。它每次读取2个字节(或4个字节,具体取决于字符集)。相比之前的FileInputStream,这是一次明显巨大的改进!

所以流操作就像这样:

int c;
while ( (c = fileReader.read()) != -1) { // some logic }
请注意,两个类都使用整数变量来存储从输入文件中检索到的值(因此,在获取时每个字符都转换为整数,而在存储时则转换回字符)。 这个类的唯一优点在于它仅处理文本文件,因此您不必指定字符集和其他一些属性。对于大多数文本文件处理情况,它提供了一个开箱即用的解决方案。它还支持国际化和本地化。 但是,它仍然非常慢(想象一下每次读取2个字节并循环遍历它!)。 缓冲流 为了解决连续循环一个或两个字节的问题,Java程序员添加了另一项出色的功能:“在处理之前创建数据缓冲区。” 当用户在YouTube上播放视频时,视频会被缓冲以提供无缝的观看体验(尽管浏览器会一直缓冲,直到整个视频都被预先缓冲)。BufferedReader类使用相同的技术。 BufferedReader对象将FileReader对象作为输入,后者包含有关需要读取的文本文件的所有必要信息(例如文件路径和字符集)。
BufferedReader br = new BufferedReader( new FileReader("example.txt") );
当对BufferedReader对象执行“读取”指令时,它使用FileReader对象从文件中读取数据。当给出指令后,FileReader对象每次读取2(或4)个字节,并将数据返回给BufferedReader,直到它遇到'\n'或'\r\n'(换行符号)。一旦一行被缓存,读取器会耐心等待,直到下一个缓冲区指令被给出。与此同时,BufferReader对象创建一个特殊的内存空间(在RAM上),称为“缓冲区”,并存储从FileReader对象中获取的所有数据。
// this variable points to the buffered line
String line;

// Keep buffering the lines and print it.
while ((line = br.readLine()) != null) {
    printWriter.println(line);
}

现在,不是每次读取两个字节,而是一整行数据被提取并存储在RAM中。当你处理完数据后,可以将整行数据存回硬盘中。这比每次读取两个字节要快得多。

但是,为什么我们需要将FileReader对象传递给BufferedReader呢?我们不能只说“缓冲这个文件”,然后让BufferedReader来处理剩下的吗?那不是很方便吗?

事实上,BufferedReader类只知道如何创建缓冲区并存储输入数据。它与数据来源的对象无关。因此,同一个对象可以用于许多其他输入流,而不仅仅是文本文件。

因此,当您将FileReader对象作为输入提供时,它会缓冲该文件,就像如果您提供了InputStreamReader作为对象,它会缓冲终端/控制台输入数据直到遇到换行符号。

// Object that reads console inputs
InputStreamReader console = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(console);
System.out.println(br.readLine());

使用这种方式,您可以使用同一个BufferReader类读取(或缓冲)多个流,例如文本文件、控制台、打印机、网络数据等,您需要记住的是:

 bufferedReader.readLine();

打印您已缓冲的任何内容。


3
谢谢!我只是试图解释一些复杂的概念! :) - Mehul Katpara
非常好的解释,它彻底地消除了我的疑虑。 :) - Akoder
太棒了!这比我从文档中阅读的内容清晰多了。 - jason adams
回答不错,但我认为你的最后一行代码是错误的。BufferedReader需要一个“Reader”对象。System.in是一个输入流。请查看被接受的答案。 - gallickgunner
非常好的解释。在阅读了几篇文档后,我并没有理解这个概念。但是通过阅读您的解释,我现在已经理解了。非常感谢。 - Maruthi Srinivas
显示剩余3条评论

89

简单来说:

FileReader类是从文件中读取字符的通用工具。BufferedReader类可以包装读取器,如FileReader,以缓冲输入并提高效率。因此,您不会使用其中之一,而是通过将FileReader对象传递给BufferedReader构造函数同时使用它们两个。

详细解释:

FileReader用于从磁盘文件中输入字符数据。输入文件可以是普通的ASCII文本文件,每个字符一个字节。读取器流会自动将磁盘文件格式中的字符转换为内部char格式。输入文件中的字符可能来自UTF格式支持的其他字母表,这种情况下每个字符最多有三个字节。同样,在这种情况下,文件中的字符也会被转换成char格式。

enter image description here

与输出一样,使用缓冲区以提高效率是一个好习惯。请使用BufferedReader实现此功能。这是我们一直在使用的键盘输入类。这些代码应该很熟悉:

BufferedReader stdin =
    new BufferedReader(new InputStreamReader( System.in ));
这些代码创建了一个 BufferedReader,但将其连接到了键盘的输入流而不是文件。
来源:http://www.oopweb.com/Java/Documents/JavaNotes/Volume/chap84/ch84_3.html

0

BufferedReader需要一个Reader,而FileReader就是其中之一——它继承自InputStreamReader,后者又继承自Reader。


-1

-1

FileReader类有助于在文件上进行写入,但其效率较低,因为它必须从文件中逐个检索一个字符,但BufferedReader可以获取数据块并将其存储在缓冲区中,因此使用缓冲区而不是从文件中逐个检索一个字符,检索变得更加容易。


-6

BufferedReader - 这是一种可以用作Scanner方法替代的方法,它可以获取文件和输入。

FileReader - 顾名思义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接