CodeGym /課程 /JAVA 25 SELF /讀寫檔案時的編碼設定

讀寫檔案時的編碼設定

JAVA 25 SELF
等級 37 , 課堂 2
開放

1. 前言

直說吧:如果你曾在俄文文字的位置看到像 Привет 這樣的字串,你已經是編碼錯誤的受害者。這通常發生在檔案以某種編碼寫入,卻用另一種編碼讀取。例如,檔案存成 UTF-8,卻以 Windows-1251 讀取,或反之亦然。

Java 預設 會使用系統編碼,你可以這樣查:

System.out.println(System.getProperty("file.encoding"));

在某些電腦上可能是 UTF-8,在另一些則是 Windows-1251,還有些會是 ISO-8859-1。因此,最好永遠明確指定編碼。這在你處理多語系資料、檔案會在不同電腦或其他程式中使用、或希望你的程式在任何環境都表現一致(而不是只在你的機器上正常)時尤其重要。

Charset 類:編碼世界中的好夥伴

在 Java 中,處理編碼要使用 java.nio.charset.Charset。 它允許以名稱指定編碼(例如 "UTF-8"),也可以使用標準常數(StandardCharsets.UTF_8)。

常見標準編碼範例:

編碼 Java 常數
UTF-8
StandardCharsets.UTF_8
UTF-16
StandardCharsets.UTF_16
ISO-8859-1
StandardCharsets.ISO_8859_1
Windows-1251
Charset.forName("Windows-1251")

建議優先使用常數:比較不容易拼錯名稱,也不會拋出 UnsupportedCharsetException

2. 指定編碼讀取檔案

舊式作法:

import java.io.*;
import java.nio.charset.StandardCharsets;

BufferedReader reader = new BufferedReader(
    new InputStreamReader(
        new FileInputStream("example.txt"),
        StandardCharsets.UTF_8 // <-- 明確指定編碼
    )
);

現代作法:

import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedReader;

BufferedReader reader = Files.newBufferedReader(
    Paths.get("example.txt"),
    StandardCharsets.UTF_8 // <-- 明確指定編碼
);

建議使用第二種方式——更簡短、更安全,且與 try-with-resources 很好搭配。

範例:從檔案讀取一行

try (BufferedReader reader = Files.newBufferedReader(
        Paths.get("hello.txt"),
        StandardCharsets.UTF_8)) {
    String line = reader.readLine();
    System.out.println("讀到:" + line);
}

為什麼這很重要: 如果檔案以 UTF-8 儲存,卻用 Windows-1251 讀取,西里爾字元會變形成亂碼。只要指定正確的編碼,文字在任何作業系統上都能被正確讀取。

3. 指定編碼寫入檔案

舊式作法:

import java.io.*;
import java.nio.charset.StandardCharsets;

BufferedWriter writer = new BufferedWriter(
    new OutputStreamWriter(
        new FileOutputStream("example.txt"),
        StandardCharsets.UTF_8 // <-- 明確指定編碼
    )
);

現代作法:

import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedWriter;

BufferedWriter writer = Files.newBufferedWriter(
    Paths.get("example.txt"),
    StandardCharsets.UTF_8 // <-- 明確指定編碼
);

範例:將一行寫入檔案

try (BufferedWriter writer = Files.newBufferedWriter(
        Paths.get("hello.txt"),
        StandardCharsets.UTF_8)) {
    writer.write("你好,世界!");
}

結果: 檔案會以 UTF-8 儲存,且可在任何支援 UTF-8 的編輯器中正確開啟。

4. 有用的細節

如何查出支援哪些編碼

import java.nio.charset.Charset;

public class ListCharsets {
    public static void main(String[] args) {
        System.out.println("可用的編碼:");
        Charset.availableCharsets().forEach((name, charset) -> System.out.println(name));
    }
}

建議: 如果你使用了較為冷門的編碼(例如古漢字或外星表情符號),請確認你的 JVM 是否支援。

使用 try-with-resources:別忘了關閉串流

處理檔案時,務必關閉串流以避免資源外洩。現代的 Java 程式會使用 try-with-resources 結構:

try (BufferedReader reader = Files.newBufferedReader(path, charset)) {
    // 處理檔案
}

就算發生錯誤,串流也會自動關閉。

建議

  • 最好一律明確指定編碼 在讀寫檔案時,即使你覺得「預設也能正常運作」。
  • 新檔案請使用 UTF-8——這已是事實上的標準,尤其在 web、JSON、XML 等情境,或希望你的檔案到處都可讀取時。
  • 對於舊檔案(例如 1C 的匯出、舊資料庫、Windows 的 CSV),請使用它們當初儲存時的編碼(例如 Windows-1251ISO-8859-1)。
  • 不要使用過時的類別,因為無法明確指定編碼:FileReader/FileWriter。改用 InputStreamReader/OutputStreamWriter 並明確指定編碼,或使用 Files 的方法。
  • 處理大型檔案時 請使用緩衝(BufferedReader/BufferedWriter),以免耗盡記憶體。

5. 編碼相關的常見錯誤

錯誤 №1:讀寫檔案時未指定編碼。
若未指定,Java 會使用系統預設("file.encoding")。在你的電腦上可能沒問題,但同事那邊就會變成「亂碼」。

錯誤 №2:讀寫使用了不同的編碼。
檔案以一種編碼寫入,卻以另一種編碼讀取。例如,檔案以 UTF-8 寫入,卻以 Windows-1251 讀取——西里爾字元會被扭曲。

錯誤 №3:使用過時的 FileReader/FileWriter 類別。
這些類別無法明確指定編碼——不建議使用。請改用 InputStreamReader/OutputStreamWriter 並指定編碼,或使用 Files 的方法。

錯誤 №4:編碼名稱拼寫錯誤。
例如寫成 "utf8" 而不是 "UTF-8",或 "win1251" 而不是 "Windows-1251"。Java 會拋出 UnsupportedCharsetException

錯誤 №5:未關閉串流——檔案未完整寫入。
如果不使用 try-with-resources 或未明確關閉串流,部分資料可能無法寫入到磁碟。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION