首页 课程 师资 教程 报名

教你Java如何读取文件

  • 2022-10-31 10:32:44
  • 5699次 动力节点

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

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

其次,我们将了解如何使用 BufferedReader、Scanner、StreamTokenizer、DataInputStream、SequenceInputStream和FileChannel读取内容。我们还将讨论如何读取 UTF-8 编码文件。

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

设置

1.输入文件

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

Hello, world!

对于一些示例,我们将使用不同的文件;在这些情况下,我们将明确提及文件及其内容。

2.辅助方法

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

测试将共享一个通用的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("
");
        }
    }
  return resultStringBuilder.toString();
}

请注意,还有其他方法可以实现相同的结果。

从类路径中读取文件

1.使用标准 Java

本节说明如何读取类路径中可用的文件。我们将阅读src/main/resources下的“ fileTest.txt ” :

@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));
}

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

ClassLoader实例也可以使用相同的方法:

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

我们使用getClass().getClassLoader()获取当前类的类加载器。

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

当用于Class instance时,路径可以是相对于包的路径,也可以是绝对路径,由前导斜杠暗示。

当然,请注意,在实践中,打开的流应该始终是关闭的,例如我们示例中的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();
        }
    }
}

2.使用commons-io库

另一个常见的选项是使用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());
}

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

同一个库还提供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());
}

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

使用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);
}

请注意,当到达文件末尾时, readLine()将返回null 。

使用 Java NIO 从文件中读取

在 JDK7 中,NIO 包进行了重大更新。

让我们看一个使用Files类和readAllLines 方法的示例。readAllLines方法 接受一个路径。

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

1.读取小文件

以下代码显示了如何使用新的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);
}

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

2.读取大文件

如果我们想用Files类读取一个大文件,我们可以使用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);
}

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

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

让我们看一个如何将数据读入字节并使用 UTF-8 字符集对其进行解码的示例。

以下代码使用 new 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("
"));
    lines.close();         
    Assert.assertEquals(expectedData, data.trim());
}

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

正如我们所见,Files API 提供了另一种将文件内容读入字符串的简单方法。

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

用扫描仪阅读

接下来让我们使用扫描仪从文件中读取。在这里,我们将使用空格作为分隔符:

@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();
}

请注意,默认分隔符是空格,但多个分隔符可以与Scanner一起使用。

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

使用StreamTokenizer读取

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

标记器首先确定下一个标记是什么,字符串或数字。我们通过查看tokenizer.ttype 字段来做到这一点。

然后我们将根据这种类型读取实际的令牌:

tokenizer.nval – 如果类型是数字

tokenizer.sval – 如果类型是字符串

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

Hello 1

以下代码从文件中读取字符串和数字:

@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();
}

请注意最后如何使用文件结束标记。

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

使用DataInputStream读取

我们可以使用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);
}

使用FileChannel读取

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

以下代码使用FileChannel和RandomAccessFile从文件中读取数据字节:

@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();
}

读取 UTF-8 编码文件

现在让我们看看如何使用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);
}

从 URL 读取内容

要从 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));
}

还有其他连接到 URL 的方法。在这里,我们使用了标准 SDK 中可用的URL和URLConnection类。

从 JAR 中读取文件

要读取位于 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));
}

在这里,我们要加载位于 Hamcrest 库中的LICENSE.txt ,因此我们将使用有助于获取资源的Matcher类。也可以使用类加载器加载相同的文件。如果大家想了解更多相关知识,不妨来关注一下本站的Java在线学习,里面的课程内容细致全面,很适合没有基础的小伙伴学习,希望对大家能够有所帮助哦。

选你想看

你适合学Java吗?4大专业测评方法

代码逻辑 吸收能力 技术学习能力 综合素质

先测评确定适合在学习

在线申请免费测试名额
价值1998元实验班免费学
姓名
手机
提交