How to Read a File in Java – 如何在Java中读取一个文件

最后修改: 2016年 10月 8日

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

1. Overview

1.概述

In this tutorial, we’ll explore different ways to read from a File in Java.

在本教程中,我们将探讨在Java中从文件中读取的不同方法

First, we’ll learn how to load a file from the classpath, a URL, or from a JAR file using standard Java classes.

首先,我们将学习如何使用标准的Java类从classpath、URL或JAR文件中加载一个文件。

Second, we’ll see how to read the content with BufferedReader, Scanner, StreamTokenizer, DataInputStream, SequenceInputStream, and FileChannel. We will also discuss how to read a UTF-8 encoded file.

其次,我们将看到如何用BufferedReaderScannerStreamTokenizerDataInputStreamSequenceInputStreamFileChannel读取内容。我们还将讨论如何读取一个UTF-8编码的文件。

Finally, we’ll explore the new techniques to load and read a file in Java 7 and Java 8.

最后,我们将探讨在Java 7和Java 8中加载和读取文件的新技术。

This article is part of the “Java – Back to Basic” series on Baeldung.

本文是Baeldung网站上“Java – 回到基础 “系列的一部分。

2. Setup

2.设置

2.1. Input File

2.1.输入文件

In most examples throughout this article, we’ll read a text file with filename fileTest.txt that contains one line:

在本文的大多数例子中,我们将读取一个文件名为fileTest.txt的文本文件,其中包含一行。

Hello, world!

For a few examples, we’ll use a different file; in these cases, we’ll mention the file and its contents explicitly.

在一些例子中,我们会使用不同的文件;在这些情况下,我们会明确提及该文件及其内容。

2.2. Helper Method

2.2.帮助者方法

We’ll use a set of test examples with core Java classes only, and in the tests, we’ll use assertions with Hamcrest matchers.

我们将使用一组只包含核心Java类的测试实例,在测试中,我们将使用带有Hamcrest匹配器的断言。

Tests will share a common readFromInputStream method that transforms an InputStream to String for easier asserting of results:

测试将共享一个共同的readFromInputStream 方法,转换一个InputStream String以便更容易断言结果:

private String readFromInputStream(InputStream inputStream)
  throws IOException {
    StringBuilder resultStringBuilder = new StringBuilder();
    try (BufferedReader br
      = new BufferedReader(new InputStreamReader(inputStream))) {
        String line;
        while ((line = br.readLine()) != null) {
            resultStringBuilder.append(line).append("\n");
        }
    }
  return resultStringBuilder.toString();
}

Note that there are other ways of achieving this same result. We can consult this article for some alternatives.

注意,还有其他方法可以实现这个相同的结果。我们可以参考这篇文章了解一些替代方案。

3. Reading a File from the Classpath

3.从Classspath读取文件

3.1. Using Standard Java

3.1.使用标准Java

This section explains how to read a file that is available on a classpath. We’ll read the “fileTest.txt” available under src/main/resources:

本节解释了如何读取classpath上可用的文件。我们将读取fileTest.txt下可用的src/main/resources

@Test
public void givenFileNameAsAbsolutePath_whenUsingClasspath_thenFileData() {
    String expectedData = "Hello, world!";
    
    Class clazz = FileOperationsTest.class;
    InputStream inputStream = clazz.getResourceAsStream("/fileTest.txt");
    String data = readFromInputStream(inputStream);

    Assert.assertThat(data, containsString(expectedData));
}

In the above code snippet, we used the current class to load a file using getResourceAsStream method and passed the absolute path of the file to load.

在上面的代码片段中,我们使用getResourceAsStream方法使用当前类加载一个文件,并传递了要加载的文件的绝对路径。

The same method is available on a ClassLoader instance as well:

同样的方法在ClassLoader 实例上也是可用的:

ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("fileTest.txt");
String data = readFromInputStream(inputStream);

We obtain the classLoader of the current class using getClass().getClassLoader().

我们使用getClass().getClassLoader()获得当前类的

The main difference is that when using the getResourceAsStream on a ClassLoader instance, the path is treated as absolute starting from the root of the classpath.

主要区别在于,当使用getResourceAsStream ClassLoader 实例上,路径被视为从classpath的根开始的绝对路径。

When used against a Class instance, the path could be relative to the package, or an absolute path, which is hinted by the leading slash.

当针对实例使用时,路径可以是相对包,或者是绝对路径,这由前面的斜线提示。

Of course, note that in practice, open streams should always be closed, such as the InputStream in our example:

当然,请注意,在实践中,开放的流应该总是被关闭的,例如我们例子中的InputStream

InputStream inputStream = null;
try {
    File file = new File(classLoader.getResource("fileTest.txt").getFile());
    inputStream = new FileInputStream(file);
    
    //...
}     
finally {
    if (inputStream != null) {
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.2. Using the commons-io Library

3.2.使用commons-io图书馆

Another common option is using the FileUtils class of the commons-io package:

另一个常见的选择是使用commons-io包的FileUtils类:

@Test
public void givenFileName_whenUsingFileUtils_thenFileData() {
    String expectedData = "Hello, world!";
        
    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(classLoader.getResource("fileTest.txt").getFile());
    String data = FileUtils.readFileToString(file, "UTF-8");
        
    assertEquals(expectedData, data.trim());
}

Here we pass the File object to the method readFileToString() of FileUtils class. This utility class manages to load the content without the necessity of writing any boilerplate code to create an InputStream instance and read data.

这里我们将文件对象传递给FileUtils类的readFileToString()方法。这个实用程序类管理加载内容,而不需要编写任何模板代码来创建一个InputStream实例并读取数据。

The same library also offers the IOUtils class:

同一个库还提供了IOUtils类:

@Test
public void givenFileName_whenUsingIOUtils_thenFileData() {
    String expectedData = "Hello, world!";
        
    FileInputStream fis = new FileInputStream("src/test/resources/fileTest.txt");
    String data = IOUtils.toString(fis, "UTF-8");
        
    assertEquals(expectedData, data.trim());
}

Here we pass the FileInputStream object to the method toString() of IOUtils class. This utility class acts in the same way as the previous one in order to create an InputStream instance and read data.

这里我们将FileInputStream对象传递给IOUtils类的toString()方法。这个实用程序类的作用与前一个相同,以创建一个InputStream实例并读取数据。

4. Reading with BufferedReader

4.用BufferedReader阅读

Now let’s focus on different ways to parse the content of a file.

现在让我们集中讨论解析文件内容的不同方法。

We’ll start with a simple way to read from a file using BufferedReader:

我们将从使用BufferedReader从文件中读取的一个简单方法开始:

@Test
public void whenReadWithBufferedReader_thenCorrect()
  throws IOException {
     String expected_value = "Hello, world!";
     String file ="src/test/resources/fileTest.txt";
     
     BufferedReader reader = new BufferedReader(new FileReader(file));
     String currentLine = reader.readLine();
     reader.close();

    assertEquals(expected_value, currentLine);
}

Note that readLine() will return null when the end of the file is reached.

注意,readLine()在到达文件末端时将返回null

5. Reading from a File Using Java NIO

5.使用Java NIO从文件中读取数据

In JDK7, the NIO package was significantly updated.

在JDK7中,NIO包被大幅更新。

Let’s look at an example using the Files class and the readAllLines method. The readAllLines method accepts a Path.

让我们看一个使用Files类和readAllLines方法的例子readAllLines 方法接受一个Path。

Path class can be considered an upgrade of the java.io.File with some additional operations in place.

Path类可以被认为是java.io.File的升级版,其中有一些额外的操作。

5.1. Reading a Small File

5.1.读取一个小文件

The following code shows how to read a small file using the new Files class:

下面的代码显示了如何使用新的Files类来读取一个小文件。

@Test
public void whenReadSmallFileJava7_thenCorrect()
  throws IOException {
    String expected_value = "Hello, world!";

    Path path = Paths.get("src/test/resources/fileTest.txt");

    String read = Files.readAllLines(path).get(0);
    assertEquals(expected_value, read);
}

Note that we can use the readAllBytes() method as well if we need binary data.

注意,如果我们需要二进制数据,我们也可以使用readAllBytes()方法。

5.2. Reading a Large File

5.2.读取大文件

If we want to read a large file with Files class, we can use the BufferedReader.

如果我们想用Files类读取一个大文件,我们可以使用BufferedReader.

The following code reads the file using the new Files class and BufferedReader:

下面的代码使用新的Files类和BufferedReader来读取文件。

@Test
public void whenReadLargeFileJava7_thenCorrect()
  throws IOException {
    String expected_value = "Hello, world!";

    Path path = Paths.get("src/test/resources/fileTest.txt");

    BufferedReader reader = Files.newBufferedReader(path);
    String line = reader.readLine();
    assertEquals(expected_value, line);
}

5.3. Reading a File Using Files.lines()

5.3.使用Files.lines()读取文件

JDK8 offers the lines() method inside the Files class. It returns a Stream of String elements.

JDK8在Files类中提供了lines()方法。它返回一个字符串元素的Stream

Let’s look at an example of how to read data into bytes and decode it using UTF-8 charset.

让我们看一个如何将数据读成字节并使用UTF-8字符集解码的例子。

The following code reads the file using the new Files.lines():

下面的代码使用新的Files.lines()读取文件。

@Test
public void givenFilePath_whenUsingFilesLines_thenFileData() {
    String expectedData = "Hello, world!";
         
    Path path = Paths.get(getClass().getClassLoader()
      .getResource("fileTest.txt").toURI());
         
    Stream<String> lines = Files.lines(path);
    String data = lines.collect(Collectors.joining("\n"));
    lines.close();
         
    Assert.assertEquals(expectedData, data.trim());
}

Using Stream with IO channels like file operations, we need to close the stream explicitly using the close() method.

使用流与IO通道,如文件操作,我们需要使用close()方法显式关闭流。

As we can see, the Files API offers another easy way to read the file contents into a String.

正如我们所看到的,Files API提供了另一种简单的方法来将文件内容读入String.

In the next sections, we’ll look at other less common methods of reading a file that may be appropriate in some situations.

在接下来的章节中,我们将看看其他不太常见的读取文件的方法,这些方法在某些情况下可能是合适的。

6. Reading with Scanner

6.用扫描器阅读

Next let’s use a Scanner to read from the File. Here we’ll use whitespace as the delimiter:

接下来让我们使用Scanner从文件中读取。这里我们将使用空格作为分隔符。

@Test
public void whenReadWithScanner_thenCorrect()
  throws IOException {
    String file = "src/test/resources/fileTest.txt";
    Scanner scanner = new Scanner(new File(file));
    scanner.useDelimiter(" ");

    assertTrue(scanner.hasNext());
    assertEquals("Hello,", scanner.next());
    assertEquals("world!", scanner.next());

    scanner.close();
}

Note that the default delimiter is the whitespace, but multiple delimiters can be used with a Scanner.

注意,默认的定界符是空白,但可以用Scanner使用多个定界符。

The Scanner class is useful when reading content from the console, or when the content contains primitive values, with a known delimiter (eg: a list of integers separated by space).

Scanner类在从控制台读取内容,或者内容包含原始值时非常有用,它有一个已知的分隔符(例如:用空格分隔的整数列表)。

7. Reading with StreamTokenizer

7.用StreamTokenizer阅读

Now let’s read a text file into tokens using a StreamTokenizer.

现在让我们使用StreamTokenizer将一个文本文件读成令牌。

The tokenizer works by first figuring out what the next token is, String or number. We do that by looking at the tokenizer.ttype field.

标记器的工作原理是首先弄清楚下一个标记是什么,是字符串还是数字。我们通过查看tokenizer.ttype字段来做到这一点。

Then we’ll read the actual token based on this type:

然后,我们将根据这个类型来读取实际的令牌。

  • tokenizer.nval – if the type was a number
  • tokenizer.sval – if the type was a String

In this example, we’ll use a different input file which simply contains:

在这个例子中,我们将使用一个不同的输入文件,其中只包含。

Hello 1

The following code reads from the file both the String and the number:

下面的代码从文件中读取了字符串和数字。

@Test
public void whenReadWithStreamTokenizer_thenCorrectTokens()
  throws IOException {
    String file = "src/test/resources/fileTestTokenizer.txt";
   FileReader reader = new FileReader(file);
    StreamTokenizer tokenizer = new StreamTokenizer(reader);

    // token 1
    tokenizer.nextToken();
    assertEquals(StreamTokenizer.TT_WORD, tokenizer.ttype);
    assertEquals("Hello", tokenizer.sval);

    // token 2    
    tokenizer.nextToken();
    assertEquals(StreamTokenizer.TT_NUMBER, tokenizer.ttype);
    assertEquals(1, tokenizer.nval, 0.0000001);

    // token 3
    tokenizer.nextToken();
    assertEquals(StreamTokenizer.TT_EOF, tokenizer.ttype);
    reader.close();
}

Note how the end of file token is used at the end.

请注意文件末尾的标记是如何使用的。

This approach is useful for parsing an input stream into tokens.

这种方法对于将输入流解析为标记非常有用。

8. Reading with DataInputStream

8.用DataInputStream阅读

We can use DataInputStream to read binary or primitive data types from a file.

我们可以使用DataInputStream来从文件中读取二进制或原始数据类型。

The following test reads the file using a DataInputStream:

下面的测试使用DataInputStream读取文件。

@Test
public void whenReadWithDataInputStream_thenCorrect() throws IOException {
    String expectedValue = "Hello, world!";
    String file ="src/test/resources/fileTest.txt";
    String result = null;

    DataInputStream reader = new DataInputStream(new FileInputStream(file));
    int nBytesToRead = reader.available();
    if(nBytesToRead > 0) {
        byte[] bytes = new byte[nBytesToRead];
        reader.read(bytes);
        result = new String(bytes);
    }

    assertEquals(expectedValue, result);
}

9. Reading with FileChannel

9.用FileChannel阅读

If we are reading a large file, FileChannel can be faster than standard IO.

如果我们要读取一个大文件,FileChannel可以比标准IO更快。

The following code reads data bytes from the file using FileChannel and RandomAccessFile:

以下代码使用FileChannelRandomAccessFile从文件读取数据字节。

@Test
public void whenReadWithFileChannel_thenCorrect()
  throws IOException {
    String expected_value = "Hello, world!";
    String file = "src/test/resources/fileTest.txt";
    RandomAccessFile reader = new RandomAccessFile(file, "r");
    FileChannel channel = reader.getChannel();

    int bufferSize = 1024;
    if (bufferSize > channel.size()) {
        bufferSize = (int) channel.size();
    }
    ByteBuffer buff = ByteBuffer.allocate(bufferSize);
    channel.read(buff);
    buff.flip();
    
    assertEquals(expected_value, new String(buff.array()));
    channel.close();
    reader.close();
}

10. Reading a UTF-8 Encoded File

10.读取UTF-8编码的文件

Now let’s see how to read a UTF-8 encoded file using BufferedReader. In this example, we’ll read a file that contains Chinese characters:

现在让我们看看如何使用BufferedReader来读取一个UTF-8编码的文件。在这个例子中,我们将读取一个包含中文字符的文件。

@Test
public void whenReadUTFEncodedFile_thenCorrect()
  throws IOException {
    String expected_value = "青空";
    String file = "src/test/resources/fileTestUtf8.txt";
    BufferedReader reader = new BufferedReader
      (new InputStreamReader(new FileInputStream(file), "UTF-8"));
    String currentLine = reader.readLine();
    reader.close();

    assertEquals(expected_value, currentLine);
}

11. Reading Content from URL

11.从URL读取内容

To read content from a URL, we will use “/” URL in our example:

要从一个URL读取内容,在我们的例子中我们将使用”/“URL。

@Test
public void givenURLName_whenUsingURL_thenFileData() {
    String expectedData = "Baeldung";

    URL urlObject = new URL("/");
    URLConnection urlConnection = urlObject.openConnection();
    InputStream inputStream = urlConnection.getInputStream();
    String data = readFromInputStream(inputStream);

    Assert.assertThat(data, containsString(expectedData));
}

There are also alternative ways of connecting to a URL. Here we used the URL and URLConnection class available in the standard SDK.

也有连接到URL的其他方法。这里我们使用了标准SDK中的URLURLConnection 类。

12. Reading a File from a JAR

12.从JAR中读取文件

To read a file which is located inside a JAR file, we will need a JAR with a file inside it. For our example, we will read “LICENSE.txt” from the “hamcrest-library-1.3.jar” file:

要读取位于JAR文件中的文件,我们需要一个JAR,里面有一个文件。在我们的例子中,我们将从”hamcrest-library-1.3.jar“文件中读取”LICENSE.txt“。

@Test
public void givenFileName_whenUsingJarFile_thenFileData() {
    String expectedData = "BSD License";

    Class clazz = Matchers.class;
    InputStream inputStream = clazz.getResourceAsStream("/LICENSE.txt");
    String data = readFromInputStream(inputStream);

    Assert.assertThat(data, containsString(expectedData));
}

Here we want to load LICENSE.txt that resides in Hamcrest library, so we will use the Matcher’s class that helps to get a resource. The same file can be loaded using the classloader too.

这里我们想加载驻扎在Hamcrest库中的LICENSE.txt,所以我们将使用Matcher的类来帮助获得资源。同样的文件也可以使用classloader来加载。

13. Conclusion

13.结论

As we can see, there are many possibilities for loading a file and reading data from it using plain Java.

正如我们所看到的,使用普通的Java加载一个文件并从中读取数据有很多可能性。

We can load a file from various locations like classpath, URL, or jar files.

我们可以从classpath、URL或jar文件等不同位置加载一个文件。

Then we can use BufferedReader to read line by line, Scanner to read using different delimiters, StreamTokenizer to read a file into tokens, DataInputStream to read binary data and primitive data types, SequenceInput Stream to link multiple files into one stream, FileChannel to read faster from large files, etc.

然后我们可以使用BufferedReader来逐行读取,Scanner来使用不同的分隔符进行读取,StreamTokenizer来将文件读成标记,DataInputStream来读取二进制数据和原始数据类型,SequenceInput Stream来将多个文件链接成一个流,FileChannel来从大文件更快读取,等等。

We can find the source code for this article in the following GitHub repo.

我们可以在下面的GitHub repo找到这篇文章的源代码。