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

Java 讀取檔案完整實作指南

     編輯:Java 相關知識

Java 檔案讀取全方位實作:從基礎到高效能

檔案讀取是 Java 開發最基礎也最常見的操作。從傳統 FileReader/BufferedReader 到 NIO.2 Files API、記憶體映射、串流處理大檔案,本文系統整理各種場景下的最佳實作模式、編碼處理、資源管理與效能優化。

核心 API 演進對比

API 代數代表類別特點適用場景
IO 流 (JDK 1.0)FileInputStream、FileReader、BufferedReader阻塞、裝飾者模式、逐行/位元組讀取中小檔案、行導向文本、相容舊代碼
NIO (JDK 1.4)FileChannel、ByteBuffer、MappedByteBuffer通道/緩衝區、非阻塞、記憶體映射大檔案、高效能、隨機存取
NIO.2 (JDK 7+)Files、Paths、Path靜態工具方法、串流式、函數式、異步現代首選、簡潔、功能豐富
Stream API (JDK 8+)Files.lines()、BufferedReader.lines()惰性串流、函數式處理、自動關閉文本逐行處理、過濾轉換、並行

現代首選:Files API 完整範例


import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;

public class FileReadExamples {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("data", "sample.txt");
        
        // 1. 一次性讀取全文 (小檔案)
        String content = Files.readString(path, StandardCharsets.UTF_8); // JDK 11+
        System.out.println("全文長度: " + content.length());

        // 2. 讀取所有行到 List (中等檔案)
        List lines = Files.readAllLines(path, StandardCharsets.UTF_8);
        System.out.println("行數: " + lines.size());

        // 3. 串流逐行處理 (大檔案、記憶體友好) - JDK 8+
        try (Stream lineStream = Files.lines(path, StandardCharsets.UTF_8)) {
            long errorCount = lineStream
                .filter(line -> line.contains("ERROR"))
                .peek(System.out::println)
                .count();
            System.out.println("錯誤行數: " + errorCount);
        }

        // 4. 並行處理 (多核加速,無序)
        try (Stream lineStream = Files.lines(path, StandardCharsets.UTF_8)) {
            Map wordFreq = lineStream
                .parallel()
                .flatMap(line -> Arrays.stream(line.split("\\s+")))
                .filter(w -> !w.isEmpty())
                .collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting()));
            System.out.println("高頻詞: " + wordFreq.entrySet().stream()
                .sorted(Map.Entry.comparingByValue().reversed())
                .limit(10).toList());
        }

        // 5. 二進位檔案讀取
        byte[] bytes = Files.readAllBytes(path); // 小檔案
        // 大檔案用 FileChannel + ByteBuffer 或 MappedByteBuffer
    }
}

傳統 IO 經典模式 (仍廣泛使用)


// 緩衝讀取器模式 - 逐行處理
public static void readWithBufferedReader(Path path) throws IOException {
    try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
        String line;
        while ((line = reader.readLine()) != null) {
            // 處理每一行
            processLine(line);
        }
    }
}

// 位元組輸入流 - 二進位檔案
public static byte[] readBytes(Path path) throws IOException {
    try (InputStream in = Files.newInputStream(path)) {
        return in.readAllBytes(); // JDK 9+
        // 或手動緩衝讀取大檔案
    }
}

大檔案高效能讀取策略

策略實作優點缺點
記憶體映射FileChannel.map(READ_ONLY, 0, size)OS 層面零拷貝、隨機存取、大檔案極快受限於虛擬記憶體、32位系統 2GB 限制、關閉延遲
直接緩衝區ByteBuffer.allocateDirect()避免堆外/堆內拷貝、DMA 友好分配/回收成本高、需手動管理
分塊串流Files.lines() / BufferedReader記憶體恆定、支援並行、背壓僅適用行導向文本
非同步 IOAsynchronousFileChannel + CompletionHandler真正非阻塞、高併發程式碼複雜、錯誤處理難

記憶體映射範例 (超大檔案)


public static void readLargeFileMapped(Path path) throws IOException {
    try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) {
        long fileSize = channel.size();
        long position = 0;
        final int MAP_SIZE = 64 * 1024 * 1024; // 64MB 每次映射
        
        while (position < fileSize) {
            long remain = fileSize - position;
            long size = Math.min(MAP_SIZE, remain);
            
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, position, size);
            // 處理 buffer 中的資料
            processBuffer(buffer);
            
            // 釋放映射 (JDK 9+ cleaner,或反射 hack)
            if (buffer instanceof sun.nio.ch.DirectBuffer) {
                ((sun.nio.ch.DirectBuffer) buffer).cleaner().clean();
            }
            position += size;
        }
    }
}

編碼處理最佳實踐

  1. 永遠顯式指定編碼:StandardCharsets.UTF_8、StandardCharsets.ISO_8859_1,避免平台預設編碼差異
  2. BOM 處理:UTF-8 BOM (EF BB BF) 需手動跳過,Files.readString JDK 17+ 自動處理
  3. 編碼偵測:未知編碼用 juniversalchardet 或 encoding-detector

路徑處理跨平台


// 正確:使用 Paths / Path API
Path path = Paths.get("data", "logs", "app.log"); // 自動用系統分隔符
Path absolute = path.toAbsolutePath();
Path normalized = path.normalize(); // 處理 . ..

// 錯誤:硬編碼分隔符
// String bad = "data/logs/app.log"; // Windows 失敗
// String bad = "data\\logs\\app.log"; // Linux 失敗

常見異常處理

異常原因處理
FileNotFoundException檔案不存在/是目錄/無權限檢查 Files.exists()/isReadable()
IOException磁碟錯誤/網路中斷/管道破裂重試、記錄、優雅降級
MalformedInputException編碼不匹配 (含非法位元組序列)指定正確編碼、用 CharsetDecoder.REPLACE
OutOfMemoryError一次性讀取超大檔案改用串流/分塊/映射讀取
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved