首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 指定編碼讀寫文件工具類:ReadWriteFileWithEncode 完整解析

     編輯:Java 程式設計入門

文件讀寫指定編碼是國際化、遷移舊系統、處理異構資料源的基礎需求。本文提供生產就緒的工具類,涵蓋緩衝流、try-with-resources、大文件分塊讀寫、編碼驗證與單元測試。

核心工具類 (Java 7+)

package com.example.util;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public final class ReadWriteFileWithEncode {

    private ReadWriteFileWithEncode() {} // 工具類禁止實例化

    /** 寫入文件:覆蓋模式,自動建立父目錄 */
    public static void write(String path, String content, String encoding) throws IOException {
        write(path, content, encoding, false);
    }

    /** 寫入文件:支援追加模式 */
    public static void write(String path, String content, String encoding, boolean append) throws IOException {
        Path filePath = Paths.get(path);
        Files.createDirectories(filePath.getParent()); // 自動建立目錄
        Charset cs = resolveCharset(encoding);
        try (BufferedWriter writer = Files.newBufferedWriter(filePath, cs,
                StandardOpenOption.CREATE, append ? StandardOpenOption.APPEND : StandardOpenOption.TRUNCATE_EXISTING)) {
            writer.write(content);
        }
    }

    /** 讀取整個文件為字串 (適合中小文件) */
    public static String read(String path, String encoding) throws IOException {
        Charset cs = resolveCharset(encoding);
        return Files.readString(Paths.get(path), cs); // Java 11+ Files.readString
    }

    /** 大文件逐行讀取處理 (避免 OOM) */
    public static void readLines(String path, String encoding, LineProcessor processor) throws IOException {
        Charset cs = resolveCharset(encoding);
        try (BufferedReader reader = Files.newBufferedReader(Paths.get(path), cs)) {
            String line;
            while ((line = reader.readLine()) != null) {
                processor.process(line);
            }
        }
    }

    /** 函數式介面:逐行處理 */
    @FunctionalInterface
    public interface LineProcessor {
        void process(String line) throws IOException;
    }

    /** 解析編碼名稱,不支援則拋異常 */
    private static Charset resolveCharset(String encoding) {
        if (encoding == null || encoding.isBlank()) {
            return StandardCharsets.UTF_8; // 預設 UTF-8
        }
        try {
            return Charset.forName(encoding.trim());
        } catch (IllegalCharsetNameException | UnsupportedCharsetException e) {
            throw new IllegalArgumentException("不支援的編碼: " + encoding, e);
        }
    }

    /** 測試主程式 */
    public static void main(String[] args) throws IOException {
        String content = "中文測試內容\nSecond line 英文";
        String path = "target/test_utf8.txt";
        String encoding = "UTF-8";

        write(path, content, encoding);
        System.out.println("寫入完成");

        String readBack = read(path, encoding);
        System.out.println("讀取結果:\n" + readBack);

        // 大文件逐行示範
        readLines(path, encoding, line -> System.out.println("[LINE] " + line));
    }
}

關鍵 API 對比 (Java 7 vs 11+)

操作Java 7 (經典)Java 11+ (現代)
讀取全文BufferedReader + StringBuilder 迴圈Files.readString(path, cs)
寫入全文BufferedWriter + write()Files.writeString(path, content, cs, options)
逐行讀取BufferedReader.readLine()Files.lines(path, cs) 返回 Stream
自動關閉try-with-resourcestry-with-resources (同)
目錄建立file.getParentFile().mkdirs()Files.createDirectories(path.getParent())

編碼未知時的完整流程

  1. 呼叫 cpDetector 或 juniversalchardet 偵測編碼。
  2. 使用偵測到的 Charset 讀取內容。
  3. 若需統一存儲,轉寫為 UTF-8:write(targetPath, content, "UTF-8")。
// 整合範例
public String convertToUtf8(String sourcePath) throws IOException {
    Charset detected = EncodingDetector.detect(new File(sourcePath)); // 參考上篇
    String content = read(sourcePath, detected.name());
    String targetPath = sourcePath + ".utf8";
    write(targetPath, content, StandardCharsets.UTF_8.name());
    return targetPath;
}

單元測試 (JUnit 5 + TemporaryDirectory)

@Test
void testReadWriteUtf8(@TempDir Path tempDir) throws IOException {
    Path file = tempDir.resolve("test.txt");
    String original = "測試內容\nLine 2";
    ReadWriteFileWithEncode.write(file.toString(), original, "UTF-8");
    assertEquals(original, ReadWriteFileWithEncode.read(file.toString(), "UTF-8"));
}

@Test
void testReadWriteGbk(@TempDir Path tempDir) throws IOException {
    Path file = tempDir.resolve("test_gbk.txt");
    String original = "中文測試";
    ReadWriteFileWithEncode.write(file.toString(), original, "GBK");
    assertEquals(original, ReadWriteFileWithEncode.read(file.toString(), "GBK"));
}

@Test
void testLargeFileStreaming(@TempDir Path tempDir) throws IOException {
    Path file = tempDir.resolve("big.txt");
    // 寫入 100 萬行
    try (BufferedWriter w = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
        for (int i = 0; i < 1_000_000; i++) w.write("Line " + i + "\n");
    }
    // 逐行驗證不載入記憶體
    AtomicInteger count = new AtomicInteger();
    ReadWriteFileWithEncode.readLines(file.toString(), "UTF-8", line -> {
        assertTrue(line.startsWith("Line "));
        count.incrementAndGet();
    });
    assertEquals(1_000_000, count.get());
}

常見坑點避坑指南

坑點後果解決
未指定編碼直接 new FileReader()使用平台預設編碼,跨平台亂碼強制指定 Charset/StandardCharsets
大文件 readString() 導致 OOM堆積耗盡改用 readLines() 流式處理
Windows \r\n 換行符讀取後變雙換行邏輯判斷錯誤readLine() 自動去除 \r\n,或手動 .replace("\r", "")
追加寫入忘記 StandardOpenOption.APPEND覆蓋原內容明確傳入 APPEND 選項
編碼名稱大小寫/別名不匹配UnsupportedCharsetException使用 Charset.forName() 統一解析,支援別名
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved