A Guide to the Java FileReader Class – Java文件读取器类指南

最后修改: 2019年 9月 8日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

As the name suggests, FileReader is a Java class that makes it easy to read the contents of a file.

顾名思义,FileReader是一个Java类,它使读取一个文件的内容变得容易

In this tutorial, we’ll learn the basic concept of a Reader and how we can use the FileReader class for doing read operations on a character stream in Java.

在本教程中,我们将学习Reader的基本概念,以及如何使用FileReader类来对Java中的字符流进行读取操作。

2. Reader Basics

2.阅读者基础知识

If we look at the code of the FileReader class, then we’ll notice that the class contains minimal code for creating a FileReader object and no other methods.

如果我们看一下FileReader类的代码,那么我们会注意到该类包含了创建FileReader对象的最小代码,没有其他方法。

This raises questions like “Who does the heavy lifting behind this class?”

这就提出了这样的问题:”谁在这个班级背后做重活?”

To answer this question, we must understand the concept and hierarchy of the Reader class in Java.

为了回答这个问题,我们必须了解Java中Reader类的概念和层次结构。

Reader is an abstract base class that makes reading characters possible through one of its concrete implementations. It defines the following basic operations of reading characters from any medium such as memory or the filesystem:

Reader是一个抽象的基类,通过它的一个具体实现使读取字符成为可能。它定义了从任何介质(如内存或文件系统)读取字符的下列基本操作。

  • Read a single character
  • Read an array of characters
  • Mark and reset a given position in a stream of characters
  • Skip position while reading a character stream
  • Close the input stream

Naturally, all the implementations of Reader class must implement all the abstract methods, namely read() and close(). Moreover, most implementations also override other inherited methods to give additional functionality or better performance.

当然,Reader类的所有实现必须实现所有的抽象方法,即read()close()。此外,大多数实现还覆盖了其他继承的方法,以提供额外的功能或更好的性能。

2.1. When to Use a FileReader

2.1.何时使用FileReader

Now that we’ve some understanding about a Reader, we’re ready to bring our focus back to the FileReader class.

现在我们已经对Reader有了一定的了解,我们准备把注意力带回FileReader类。

FileReader inherits its functionality from InputStreamReader, which is a Reader implementation designed to read bytes from an input stream as characters.

FileReaderInputStreamReader继承其功能,后者是一个Reader实现,旨在从输入流中读取字节作为字符。

Let’s see this hierarchy in the class definitions:

让我们在类的定义中看到这种层次结构。

public class InputStreamReader extends Reader {}

public class FileReader extends InputStreamReader {}

In general, we can use an InputStreamReader for reading characters from any input source.

一般来说,我们可以使用一个InputStreamReader来读取任何输入源的字符。

However, when it comes to reading text from a file, using an InputStreamReader would be like cutting an apple with a sword. Of course, the right tool would be a knife, which is precisely what FileReader promises.

然而,当涉及到从文件中读取文本时,使用InputStreamReader就像用剑切苹果。当然,正确的工具应该是一把刀,这正是FileReader所承诺的。

We can use a FileReader when we want to read text from a file using the system’s default character set. For any other advanced functionality, it’d be ideal for making use of InputStreamReader class directly.

我们可以使用FileReader,当我们想使用系统的默认字符集从文件中读取文本时。对于任何其他高级功能,直接使用InputStreamReader类是最理想的。

3. Reading a Text File with a FileReader

3.用FileReader读取一个文本文件

Let’s walk through a coding exercise of reading characters from a HelloWorld.txt file using a FileReader instance.

让我们通过一个编码练习,使用FileReader 实例从HelloWorld.txt文件中读取字符。

3.1. Creating a FileReader

3.1.创建一个文件读取器

As a convenience class, FileReader offers three overloaded constructors that can be used to initialize a reader that can read from a file as an input source.

作为一个方便的类,FileReader提供了三个重载的构造函数,可用于初始化一个可以从文件中读取输入源的阅读器。

Let’s take a look at these constructors:

让我们来看看这些构造函数。

public FileReader(String fileName) throws FileNotFoundException {
    super(new FileInputStream(fileName));
}

public FileReader(File file) throws FileNotFoundException {
    super(new FileInputStream(file));
}

public FileReader(FileDescriptor fd) {
    super(new FileInputStream(fd));
}

In our case, we know the filename of the input file. Consequently, we can use the first constructor to initialize a reader:

在我们的例子中,我们知道输入文件的文件名。因此,我们可以使用第一个构造函数来初始化一个阅读器。

FileReader fileReader = new FileReader(path);

3.2. Reading a Single Character

3.2.读取单个字符

Next, let’s create readAllCharactersOneByOne(), a method for reading characters from the file one at a time:

接下来,让我们创建readAllCharactersOneByOne(),一个用于从文件中逐个读取字符的方法。

public static String readAllCharactersOneByOne(Reader reader) throws IOException {
    StringBuilder content = new StringBuilder();
    int nextChar;
    while ((nextChar = reader.read()) != -1) {
        content.append((char) nextChar);
    }
    return String.valueOf(content);
}

As we can see from the above code, we’ve used the read() method in a loop to read characters one by one until it returns -1, meaning there’re no more characters to read.

从上面的代码中我们可以看到,我们已经使用了 read()方法在一个循环中逐个读取字符,直到它返回-1,意思是没有更多的字符可以读取。

Now, let’s test our code by validating that the text read from the file matches the expected text:

现在,让我们通过验证从文件中读取的文本与预期的文本相符来测试我们的代码。

@Test
public void givenFileReader_whenReadAllCharacters_thenReturnsContent() throws IOException {
    String expectedText = "Hello, World!";
    File file = new File(FILE_PATH);
    try (FileReader fileReader = new FileReader(file)) {
        String content = FileReaderExample.readAllCharactersOneByOne(fileReader);
        Assert.assertEquals(expectedText, content);
    }
}

3.3. Reading of an Array of Characters

3.3.读取一个字符阵列

We can even read multiple characters at once using the inherited read(char cbuf[], int off, int len) method:

我们甚至可以使用继承的read(char cbuf[], int off, int len)方法一次性读取多个字符。

public static String readMultipleCharacters(Reader reader, int length) throws IOException {
    char[] buffer = new char[length];
    int charactersRead = reader.read(buffer, 0, length);
    if (charactersRead != -1) {
        return new String(buffer, 0, charactersRead);
    } else {
        return "";
    }
}

There’s a subtle difference in the return value of read() when it comes to reading multiple characters in an array. The return value here is either the number of characters read or -1 if the reader has reached the end of the input stream.

当涉及到读取数组中的多个字符时,read()的返回值有一个微妙的区别。这里的返回值是读取的字符数,或者是-1(如果阅读器已经到达输入流的末端)。

Next, let’s test the correctness of our code:

接下来,让我们测试一下我们的代码的正确性。

@Test
public void givenFileReader_whenReadMultipleCharacters_thenReturnsContent() throws IOException {
    String expectedText = "Hello";
    File file = new File(FILE_PATH);
    try (FileReader fileReader = new FileReader(file)) {
        String content = FileReaderExample.readMultipleCharacters(fileReader, 5);
        Assert.assertEquals(expectedText, content);
    }
}

4. Limitations

4.限制条件

We’ve seen that the FileReader class relies on the default system character encoding.

我们已经看到,FileReader类依赖于默认的系统字符编码。

So, for situations, where we need to use custom values for the character set, buffer size, or input stream, we must use InputStreamReader.

因此,对于需要对字符集、缓冲区大小或输入流使用自定义值的情况,我们必须使用InputStreamReader

Moreover, we all know that I/O cycles are expensive and can introduce latency to our application. So, it’s in our best interest to minimize the number of I/O operations by wrapping a BufferedReader around our FileReader object:

此外,我们都知道,I/O周期是很昂贵的,而且会给我们的应用程序带来延迟。因此,为了我们的最佳利益,在我们的FileReader对象周围包裹一个BufferedReader,以最大限度地减少I/O操作的数量。

BufferedReader in = new BufferedReader(fileReader);

5. Conclusion

5.总结

In this tutorial, we learned about the basic concepts of a Reader and how FileReader makes it simple to do read operations on text files though some examples.

在本教程中,我们了解了Reader的基本概念,以及FileReader如何通过一些例子使对文本文件进行读取操作变得简单。

As always, the complete source code for the tutorial is available on GitHub.

一如既往,该教程的完整源代码可在GitHub上获得。