如何在 Java 中新增 UTF-8 BOM
一、簡介
在文字編碼中,位元組順序標記(BOM)是檔案開頭的特殊標記,指示其位元組順序和編碼方案。對於 UTF-8 編碼,BOM 是三個位元組的序列: 0xEF
、 0xBB
和0xBF
。此外,這些位元組也會向軟體發出訊號,表明該檔案是使用 UTF-8 編碼的。
在本教程中,我們將探索在 Java 中向檔案添加 UTF-8 BOM 的不同方法,檢查位元組層級和文字層級方法,並確保處理和解釋 BOM 的一致性。
2.了解UTF-8 BOM
UTF-8 BOM 表示檔案透過特殊的位元組序列以 UTF-8 進行編碼。儘管不是強制性的,但在某些情況下包含 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 新增至檔案中,具體取決於我們使用的是位元組還是字串。
3.使用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
開啟文件,確保流在完成後自動關閉。
接下來,我們write()
文件,然後是 UTF-8 編碼的內容。
最後,我們使用Files.readString()
讀回文件,並確保文件以 BOM 開頭並包含預期的文件內容。
請注意,此方法在位元組層級運行。讀回內容會自動將 BOM 位元組轉換為其等效的 Unicode 。
4. 使用 Java Writer 編寫帶有 BOM 的 UTF-8
當用 Java 編寫帶有 BOM 的 UTF-8 檔案時,我們可以利用包裝輸出流的編寫器。 BufferedWriter
和PrintWriter
允許我們在寫入檔案內容時新增 BOM。這些方法處理編碼並提供更高層級的抽像以簡化檔案輸出。
4.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 是否位於文件的開頭,後面跟著我們的內容。
對於優先考慮文字檔案和高階編碼管理的情況,此方法更可取。
4.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()
方法新增內容。
5. 使用 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 管理。
六、結論
在本文中,我們探索了在 Java 中向檔案添加 UTF-8 位元組順序標記 (BOM) 的各種方法。
我們從基本方法開始,使用FileOutputStream
寫入 BOM 位元組。然後我們將OutputStreamWriter
與BufferedWriter
或PrintWriter
結合起來來管理BOM。
最後,我們使用 Apache Commons IO 等第三方函式庫來簡化文件處理。
與往常一樣,本文的完整程式碼範例可以在 GitHub 上找到。