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

Java 大型二進位檔案讀寫實戰:雙精度浮點數序列化與記憶體高效處理

     編輯:Java 相關知識

處理數百 MB 的二進位檔案時,一次性讀入記憶體會導致 OOM。本文實作分塊讀寫工具類,示範 double 與 byte[] 互轉、緩衝流包裝、追加寫入模式,並附效能測試數據。

檔案規格與挑戰

  • 資料來源:Fortran 程式產生,每值佔 8 bytes(IEEE 754 double)。
  • 規模:23,543 條 × 721 組 × 2 個值 ≈ 3,394 萬筆,檔案約 256 MB。
  • 目標:讀取二進位檔 → 轉十進位文字 → 寫入新檔,記憶體常駐 < 10 MB。

核心轉換工具方法


public final class DoubleBytesConverter {
    private DoubleBytesConverter() {}

    // double -> byte[8] (Little-Endian)
    public static byte[] toBytes(double d) {
        long bits = Double.doubleToRawLongBits(d);
        byte[] bytes = new byte[8];
        for (int i = 0; i < 8; i++) {
            bytes[i] = (byte) ((bits >> (8 * i)) & 0xFF);
        }
        return bytes;
    }

    // byte[8] -> double (Little-Endian)
    public static double toDouble(byte[] bytes) {
        long bits = 0;
        for (int i = 0; i < 8; i++) {
            bits |= ((long) (bytes[i] & 0xFF)) << (8 * i);
        }
        return Double.longBitsToDouble(bits);
    }
}

注意:Fortran 通常用 Little-Endian,上述位移方向需與寫入端一致。若為 Big-Endian,改用 ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getDouble()。

大檔案分塊讀寫工具類


import java.io.*;

public class LargeFileProcessor {
    private DataOutputStream binOut;
    private BufferedWriter txtOut;
    private enum Type { BINARY, TEXT }
    private Type type;

    // 開啟檔案(刪除舊檔、建立目錄)
    public boolean begin(String path, Type type) throws IOException {
        File file = new File(path);
        file.getParentFile().mkdirs();
        if (file.exists()) file.delete();
        file.createNewFile();

        this.type = type;
        switch (type) {
            case TEXT:
                txtOut = new BufferedWriter(new FileWriter(file, true));
                break;
            case BINARY:
                binOut = new DataOutputStream(new FileOutputStream(file, true));
                break;
        }
        return true;
    }

    // 追加寫入(文字:直接寫;二進位:解析空格分隔的 double 字串)
    public boolean append(String data) throws IOException {
        switch (type) {
            case TEXT:
                txtOut.append(data);
                break;
            case BINARY:
                for (String token : data.trim().split("\\s+")) {
                    binOut.write(DoubleBytesConverter.toBytes(Double.parseDouble(token)));
                }
                break;
        }
        return true;
    }

    // 關閉資源
    public boolean end() throws IOException {
        if (type == Type.TEXT) {
            txtOut.flush(); txtOut.close();
        } else {
            binOut.flush(); binOut.close();
        }
        return true;
    }
}

測試程式:256 MB 二進位 → 文字


public class FileConvertTest {
    static final String INPUT = "D:/data/velocity.bin";
    static final String OUTPUT = "D:/data/velocity.txt";

    public static void main(String[] args) throws IOException {
        LargeFileProcessor processor = new LargeFileProcessor();
        processor.begin(OUTPUT, LargeFileProcessor.Type.TEXT);

        try (DataInputStream in = new DataInputStream(
                new BufferedInputStream(new FileInputStream(INPUT)))) {

            byte[] buffer = new byte[8];
            int count = 0;
            while (in.read(buffer) != -1) {
                double val = DoubleBytesConverter.toDouble(buffer);
                processor.append(String.valueOf(val) + "\n");
                if (++count % 1_000_000 == 0) {
                    System.out.println("已處理 " + count + " 筆");
                }
            }
        }
        processor.end();
        System.out.println("轉換完成,共 " + count + " 筆");
    }
}

效能結果與分析

指標數值
輸入檔大小256 MB
輸出檔大小約 580 MB(文字膨脹 2.3 倍)
程式執行時間約 30 秒(SSD,單執行緒)
JVM 堆記憶體峰值< 20 MB

關鍵優化點:

  1. BufferedInputStream + DataInputStream 減少系統呼叫。
  2. 固定 8-byte 緩衝區,避免頻繁分配物件。
  3. 文字寫入用 BufferedWriter 批次刷新。
  4. 一次性寫入法(累積字串再寫)在大檔案會 OOM,分塊追加是正解。

擴展建議

  • 若需更高效能,可用 FileChannel + MappedByteBuffer 記憶體映射檔案。
  • 多執行緒並行處理:將檔案切分固定位移區間,各執行緒獨立讀寫。
  • 生產環境建議加入 CRC32 校驗、斷點續傳、進度持久化。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved