如何在 Java 中添加 UTF-8 BOM

在本文中,我们探讨了在 Java 中向文件添加 UTF-8 字节顺序标记 (BOM) 的各种方法。

我们从基本方法开始,使用FileOutputStream写入 BOM 字节。然后我们将 OutputStreamWriter与BufferedWriter或PrintWriter结合起来管理 BOM。

最后,我们使用 Apache Commons IO 等第三方库来简化文件处理。

在文本编码中,字节顺序标记 (BOM)是文件开头的特殊标记,用于指示其字节顺序和编码方案。对于 UTF-8 编码,BOM 是三个字节的序列:0xEF、0xBB和0xBF。此外,这些字节还向软件发出信号,表明该文件是使用 UTF-8 编码的。

在本教程中,我们将探讨在 Java 中向文件添加 UTF-8 BOM 的不同方法,研究字节级和文本级方法,并确保处理和解释 BOM 的一致性。

理解 UTF-8 BOM UTF-8 BOM 表示文件通过特殊的字节序列以 UTF-8 编码。虽然这不是强制性的,但在某些情况下包含 BOM 可能至关重要,尤其是在使用依赖 BOM 检测编码格式的旧软件或特定平台时。

正如我们上面提到的,UTF-8 BOM 由十六进制的三个字节组成:0xEF,0xBB和0xBF。

此外,Unicode 字符\uFEFF(称为零宽度不间断空格 ( ZWNBSP ) )也表示此序列。此 Unicode 字符表示 BOM 的存在,其功能与字节序列相同。

为了确保代码的一致性,我们将在本教程中将字节序列和 Unicode 表示定义为常量:

private static final byte[] UTF8_BOM = {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};
private static final String UTF8_BOM_UNICODE = "\uFEFF";
在本教程中,我们将使用原始字节或零宽度不间断空格字符将 BOM 添加到文件中,具体取决于我们处理的是字节还是字符串。

使用FileOutputStream和write方法 向文件添加 BOM 的最简单方法之一是使用 Java 的FileOutputStream,它允许我们直接写入原始字节。此方法可以控制文件中的确切字节序列,使其适合低级、面向字节的文件操作。

首先,让我们手动将文件开头的 BOM 字节写为0xEF、0xBB和0xBF,然后是 UTF-8 编码的内容:

private static final String FILE_PATH_OUTPUT_STREAM = "output_with_bom.txt";
private static final String TEST_CONTENT = "This is the content of the file";
@Test
public void givenText_whenAddingBomWithFileOutputStream_thenBOMAdded() throws IOException {
    try (FileOutputStream fos = new FileOutputStream(FILE_PATH_OUTPUT_STREAM)) {
        fos.write(UTF8_BOM);
        fos.write(TEST_CONTENT.getBytes(StandardCharsets.UTF_8));
    }
    String result = Files.readString(Path.of(FILE_PATH_OUTPUT_STREAM), StandardCharsets.UTF_8);
    assertTrue(result.startsWith(UTF8_BOM_UNICODE));
    assertTrue(result.contains(TEST_CONTENT));
}
我们首先定义文件路径、内容和表示 UTF-8 BOM 的字节数组。然后,我们在try-with-resources块中使用FileOutputStream打开文件,确保在我们完成后流自动关闭。

接下来,我们将 BOM 字节write()到文件中,然后是 UTF-8 编码的内容。

最后,我们使用Files.readString()读回文件并确保文件以 BOM 开头并包含预期的文件内容。

请注意,此方法在字节级别运行。读回内容会自动将 BOM 字节转换为其 Unicode 等效值。

使用 Java Writer 编写带 BOM 的 UTF-8 编码 在 Java 中编写带有 BOM 的 UTF-8 文件时,我们可以利用包装输出流的编写器。BufferedWriter和PrintWriter允许我们在写入文件内容时添加 BOM。这些方法处理编码并提供更高级别的抽象,以便更轻松地输出文件。

1. 使用BufferedWriter和OutputStreamWriter 将BufferedWriter与OutputStreamWriter结合使用可提供一种管理 UTF-8 文件中的 BOM 的高级方法:

private static final String FILE_PATH_BUFFERED_WRITER = "output_with_bom_buffered.txt";
@Test
public void givenText_whenAddingBomWithBufferedWriter_thenBOMAdded() throws IOException {
    try (OutputStreamWriter osw = new OutputStreamWriter(
            new FileOutputStream(FILE_PATH_BUFFERED_WRITER), StandardCharsets.UTF_8);
         BufferedWriter writer = new BufferedWriter(osw)) {
        writer.write(UTF8_BOM_UNICODE);
        writer.write(TEST_CONTENT);
    }
    String result = Files.readString(Path.of(FILE_PATH_BUFFERED_WRITER), StandardCharsets.UTF_8);
    assertTrue(result.startsWith(UTF8_BOM_UNICODE));
    assertTrue(result.contains(TEST_CONTENT));
}
在这个方法中,我们使用FileOutputStream打开文件,并使用OutputStreamWriter创建一个通道来写入文件,这还允许我们指定 UTF-8 编码。

然后,我们将流包装在BufferedWriter中,这样我们就可以以受控的方式写入文件。我们在文件开头使用 Unicode 转义序列UTF8_BOM_UNICODE写入 BOM ,然后写入实际内容。

最后,我们读回内容以验证 BOM 是否位于文件的开头,后面跟着我们的内容。

对于文本文件和更高级别的编码管理是优先考虑的情况,这种方法是更好的选择。

2. 使用PrintWriter和OutputStreamWriter 另一种选择是将PrintWriter与OutputStreamWriter结合使用。这种方法提供了一种方便的文本输出格式,尤其是对于结构化文本:

private static final String FILE_PATH_PRINT_WRITER = "output_with_bom_print_writer.txt";
@Test
public void givenText_whenUsingPrintWriter_thenBOMAdded() throws IOException {
    try (PrintWriter writer = new PrintWriter(
            new OutputStreamWriter(
              new FileOutputStream(FILE_PATH_PRINT_WRITER), StandardCharsets.UTF_8))) {
        writer.write(UTF8_BOM_UNICODE);
        writer.println(TEST_CONTENT);
    }
    String result = Files.readString(Path.of(FILE_PATH_PRINT_WRITER), StandardCharsets.UTF_8);
    assertTrue(result.startsWith(UTF8_BOM_UNICODE));
    assertTrue(result.contains(TEST_CONTENT));
}
这里,OutputStreamWriter再次指定了 UTF-8 编码,而PrintWriter提供了一种方便的方法来编写结构化文本。我们使用write()手动使用UTF8_BOM_UNICODE添加 BOM ,然后使用println()方法打印内容。

使用 Apache Commons IO Apache Commons IO简化了文件处理,我们可以利用其实用方法来处理带有 BOM 的内容写入。虽然我们仍然需要手动添加 BOM,但该库的实用方法简化了文件的写入和读取:

private static final String FILE_PATH_COMMONS_IO = "output_with_bom_commons_io.txt";
@Test
public void givenText_whenUsingCommonsIO_thenBOMAdded() throws IOException {
    byte[] bomAndContent = ArrayUtils.addAll(
      UTF8_BOM,
      TEST_CONTENT.getBytes(StandardCharsets.UTF_8)
    );
    FileUtils.writeByteArrayToFile(new File(FILE_PATH_COMMONS_IO), bomAndContent);
    String result = FileUtils.readFileToString(
      new File(FILE_PATH_COMMONS_IO), StandardCharsets.UTF_8
    );
    assertTrue(result.startsWith(UTF8_BOM_UNICODE));
    assertTrue(result.contains(TEST_CONTENT));
}
我们使用Apache Commons Lang 中的ArrayUtils.addAll()将 BOM 字节与内容字节组合成一个数组。然后,我们使用 Apache Commons IO 中的FileUtils.writeByteArrayToFile()一步写入 BOM 和内容。

FileUtils.readFileToString()将整个文件读入字符串,让我们验证 BOM 和内容。请注意,我们将 BOM 添加为原始字节,但读回时会将其解释为 Unicode 字符。

这种方法对于已经在使用 Apache Commons 库的场景特别有效,因为它们为文件 I/O 提供了有效的方法,同时简化了 BOM 管理。