在本文中,我们探讨了在 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"; |
使用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)); } |
接下来,我们将 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)); } |
然后,我们将流包装在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)); } |
使用 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)); } |
FileUtils.readFileToString()将整个文件读入字符串,让我们验证 BOM 和内容。请注意,我们将 BOM 添加为原始字节,但读回时会将其解释为 Unicode 字符。
这种方法对于已经在使用 Apache Commons 库的场景特别有效,因为它们为文件 I/O 提供了有效的方法,同时简化了 BOM 管理。