1. 简介

本教程将介绍如何使用 Guava IO 工具类来实现文件的写入与读取操作。我们将通过多个示例展示 Guava 提供的各种便捷 API,帮助开发者更优雅地处理文件 I/O。

2. 使用 Files 类进行文件写入

我们从一个最简单的例子开始:使用 Files.write() 方法将字符串写入到文件中:

@Test
public void whenWriteUsingFiles_thenWritten() throws IOException {
    String expectedValue = "Hello world";
    File file = new File("test.txt");
    Files.write(expectedValue, file, Charsets.UTF_8);
    String result = Files.toString(file, Charsets.UTF_8);
    assertEquals(expectedValue, result);
}

⚠️ 注意:还可以使用 Files.append() 方法向已存在的文件追加内容。

3. 使用 CharSink 写入文件

接下来,我们使用 CharSink 来完成写入操作。首先通过 Files.asCharSink() 获取一个 CharSink 实例,然后调用其方法写入数据:

@Test
public void whenWriteUsingCharSink_thenWritten() throws IOException {
    String expectedValue = "Hello world";
    File file = new File("test.txt");
    CharSink sink = Files.asCharSink(file, Charsets.UTF_8);
    sink.write(expectedValue);

    String result = Files.toString(file, Charsets.UTF_8);
    assertEquals(expectedValue, result);
}

✅ 此外,CharSink 还支持批量写入多行内容。例如下面这个示例中,我们将一组名字写入文件,并以空格作为分隔符:

@Test
public void whenWriteMultipleLinesUsingCharSink_thenWritten() throws IOException {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    File file = new File("test.txt");
    CharSink sink = Files.asCharSink(file, Charsets.UTF_8);
    sink.writeLines(names, " ");

    String result = Files.toString(file, Charsets.UTF_8);
    String expectedValue = Joiner.on(" ").join(names);
    assertEquals(expectedValue, result.trim());
}

4. 使用 ByteSink 写入字节

如果需要直接写入原始字节,可以使用 ByteSink。下面的示例演示了如何通过 Files.asByteSink() 获取 ByteSink 并写入字节数组:

@Test
public void whenWriteUsingByteSink_thenWritten() throws IOException {
    String expectedValue = "Hello world";
    File file = new File("test.txt");
    ByteSink sink = Files.asByteSink(file);
    sink.write(expectedValue.getBytes());

    String result = Files.toString(file, Charsets.UTF_8);
    assertEquals(expectedValue, result);
}

💡 小贴士:可以通过 byteSink.asCharSink() 轻松在 ByteSinkCharSink 之间转换。

5. 使用 Files 类读取文件

接下来,我们来看看如何使用 Files 类读取文件内容。

✅ 最简单的方式是使用 Files.toString() 一次性读取整个文件内容为字符串:

@Test
public void whenReadUsingFiles_thenRead() throws IOException {
    String expectedValue = "Hello world";
    File file = new File("test.txt");
    String result = Files.toString(file, Charsets.UTF_8);

    assertEquals(expectedValue, result);
}

此外,也可以使用 Files.readLines() 将文件内容按行读入为一个字符串列表:

@Test
public void whenReadMultipleLinesUsingFiles_thenRead() throws IOException {
    File file = new File("test.txt");
    List<String> result = Files.readLines(file, Charsets.UTF_8);

    assertThat(result, contains("John", "Jane", "Adam", "Tom"));
}

📌 若只需要读取第一行内容,可使用 Files.readFirstLine() 方法。

6. 使用 CharSource 读取文件

现在我们来看如何使用 CharSource 来读取文件内容。

首先通过 Files.asCharSource() 获取 CharSource 实例,再调用其 read() 方法获取完整内容:

@Test
public void whenReadUsingCharSource_thenRead() throws IOException {
    String expectedValue = "Hello world";
    File file = new File("test.txt");
    CharSource source = Files.asCharSource(file, Charsets.UTF_8);

    String result = source.read();
    assertEquals(expectedValue, result);
}

✅ 更有趣的是,多个 CharSource 可以合并成一个统一视图进行读取。如下示例展示了两个文件内容的拼接读取:

@Test
public void whenReadMultipleCharSources_thenRead() throws IOException {
    String expectedValue = "Hello worldTest";
    File file1 = new File("test1.txt");
    File file2 = new File("test2.txt");

    CharSource source1 = Files.asCharSource(file1, Charsets.UTF_8);
    CharSource source2 = Files.asCharSource(file2, Charsets.UTF_8);
    CharSource source = CharSource.concat(source1, source2);

    String result = source.read();
    assertEquals(expectedValue, result);
}

7. 使用 CharStreams 读取文件

我们可以借助 CharStreams 工具类,配合 FileReader 将文件内容读入为字符串:

@Test
public void whenReadUsingCharStream_thenRead() throws IOException {
    String expectedValue = "Hello world";
    FileReader reader = new FileReader("test.txt");
    String result = CharStreams.toString(reader);

    assertEquals(expectedValue, result);
    reader.close();
}

⚠️ 别忘了手动关闭 reader,避免资源泄露。

8. 使用 ByteSource 读取字节

如果需要读取原始字节内容,可以使用 ByteSource

@Test
public void whenReadUsingByteSource_thenRead() throws IOException {
    String expectedValue = "Hello world";
    File file = new File("test.txt");
    ByteSource source = Files.asByteSource(file);

    byte[] result = source.read();
    assertEquals(expectedValue, new String(result));
}

✅ 此外,ByteSource 支持从指定偏移量开始读取,这在处理大文件时尤其有用:

@Test
public void whenReadAfterOffsetUsingByteSource_thenRead() throws IOException {
    String expectedValue = "lo world";
    File file = new File("test.txt");
    long offset = 3;
    long len = 1000;

    ByteSource source = Files.asByteSource(file).slice(offset, len);
    byte[] result = source.read();
    assertEquals(expectedValue, new String(result));
}

💡 你也可以使用 byteSource.asCharSource() 转换为字符源进行读取。

9. 使用 ByteStreams 读取字节

同样,我们可以使用 ByteStreams 配合 FileInputStream 来读取字节数据:

@Test
public void whenReadUsingByteStream_thenRead() throws IOException {
    String expectedValue = "Hello world";
    FileInputStream reader = new FileInputStream("test.txt");
    byte[] result = ByteStreams.toByteArray(reader);
    reader.close();

    assertEquals(expectedValue, new String(result));
}

⚠️ 同样需要注意关闭流资源。

10. 使用 Resources 读取 ClassPath 下的文件

最后,如果你要读取的是位于 classpath 中的资源文件(如配置文件),可以使用 Resources 工具类:

@Test
public void whenReadUsingResources_thenRead() throws IOException {
    String expectedValue = "Hello world";
    URL url = Resources.getResource("test.txt");
    String result = Resources.toString(url, Charsets.UTF_8);

    assertEquals(expectedValue, result);
}

📌 这种方式非常适合读取打包进 jar 包中的静态资源。

11. 总结

本文介绍了使用 Guava IO 提供的一系列工具类和接口,如 Files, CharSink, ByteSink, CharSource, ByteSource 等,来进行文件的读写操作。

所有示例代码均可在 GitHub 项目 中找到,这是一个基于 Eclipse 的 Maven 项目,导入即可运行。

✅ Guava 的这些 IO 工具不仅简洁高效,还隐藏了许多繁琐的 try-catch 和资源管理逻辑,值得在日常开发中广泛使用。


原始标题:Guava - Write to File, Read from File

« 上一篇: Baeldung周报42
» 下一篇: Baeldung周报43