You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++读取带头部的Fortran输出二进制矩阵文件

高效读取Fortran DIRECT模式生成的二进制文件方案(Linux环境)

核心前提说明

Fortran DIRECT访问模式的无格式二进制文件没有记录边界的额外标记,所有记录按固定长度连续存储,且Linux下gfortran默认以字节为RECL(记录长度)单位。读取的关键是严格匹配Fortran写入时的记录长度、数据类型和存储顺序。

步骤1:明确Fortran写入规则

先从原Fortran代码中确认以下信息:

  • 打开文件时指定的RECL值(每条记录的字节长度)
  • 7条头部记录各自的内容、数据类型(如字符、整数、双精度浮点数)及对应字节数
  • 矩阵数据的存储方式(列主序是Fortran默认,写入时是单条大记录还是分多条列记录)

步骤2:高效读取头部信息

用C++低级IO(std::ifstream::read())直接按二进制块读取,避免字符串流等中间层。通过结构体映射Fortran的记录结构,禁止结构体字节填充确保匹配。

示例代码框架

#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <algorithm>
#include <cctype>
#include <fcntl.h> // 用于posix_fadvise优化

using namespace std;

// 按Fortran头部记录定义结构体,禁止字节填充
#pragma pack(push, 1)
// 第1条记录:256字节描述文本
struct HeaderRec1 {
    char desc[256];
};
// 第2条记录:行列数(4字节整数)
struct HeaderRec2 {
    int32_t rows;
    int32_t cols;
};
// 第3条记录:双精度参数
struct HeaderRec3 {
    double alpha;
    double beta;
};
// 补充剩余4条头部记录的结构体
#pragma pack(pop)

int main() {
    // 关闭C++与C标准IO同步,加速读取
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    ifstream infile("data.bin", ios::binary | ios::in);
    if (!infile.is_open()) {
        cerr << "文件打开失败" << endl;
        return 1;
    }

    // 告诉操作系统预读文件,优化顺序读取效率
    int fd = infile.native_handle();
    posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);

    // 读取第1条头部记录
    HeaderRec1 rec1;
    infile.read(reinterpret_cast<char*>(&rec1), sizeof(rec1));
    if (!infile) { cerr << "读取头部记录1失败" << endl; return 1; }
    // 清理Fortran字符串末尾的空格
    string desc(rec1.desc);
    desc.erase(find_if(desc.rbegin(), desc.rend(), [](int ch) {
        return !isspace(ch);
    }).base(), desc.end());
    cout << "描述:" << desc << endl;

    // 读取第2条头部记录(获取行列数)
    HeaderRec2 rec2;
    infile.read(reinterpret_cast<char*>(&rec2), sizeof(rec2));
    if (!infile) { cerr << "读取头部记录2失败" << endl; return 1; }
    const int32_t rows = rec2.rows;
    const int32_t cols = rec2.cols;
    cout << "行数:" << rows << ",列数:" << cols << endl;

    // 读取第3条及剩余4条头部记录
    HeaderRec3 rec3;
    infile.read(reinterpret_cast<char*>(&rec3), sizeof(rec3));
    if (!infile) { cerr << "读取头部记录3失败" << endl; return 1; }
    cout << "Alpha:" << rec3.alpha << ",Beta:" << rec3.beta << endl;
    // ... 读取剩余4条头部记录的代码 ...

    // 后续读取矩阵数据

步骤3:高效读取大矩阵(20-60G)

矩阵读取的核心是减少IO次数,优先大块读取,内存不足时分块处理。

方案1:内存足够时一次性读取

// 预分配矩阵内存(列主序,与Fortran一致)
    const size_t total_elements = static_cast<size_t>(rows) * cols;
    vector<double> mat(total_elements);

    // 一次性读取整个矩阵
    infile.read(reinterpret_cast<char*>(mat.data()), total_elements * sizeof(double));
    if (!infile) { cerr << "读取矩阵失败" << endl; return 1; }

    // 访问方式:mat[i + rows*j] 对应Fortran的 mat(i+1, j+1)(Fortran是1索引)
    cout << "第一个元素:" << mat[0] << endl;
    cout << "最后一个元素:" << mat[total_elements - 1] << endl;

方案2:内存不足时分块读取

// 每次读取100列(可根据内存调整块大小)
    const size_t block_cols = 100;
    const size_t block_size = static_cast<size_t>(rows) * block_cols;
    vector<double> block(block_size);
    vector<double> mat(total_elements);

    for (size_t j = 0; j < cols; j += block_cols) {
        const size_t current_cols = min(block_cols, cols - j);
        const size_t current_size = static_cast<size_t>(rows) * current_cols;
        
        infile.read(reinterpret_cast<char*>(block.data()), current_size * sizeof(double));
        if (!infile) { cerr << "读取块" << j/block_cols << "失败" << endl; return 1; }

        // 将块数据复制到矩阵对应位置
        copy(block.begin(), block.begin() + current_size, mat.begin() + static_cast<size_t>(rows)*j);
    }

编译与优化

用g++开启最高优化编译:

g++ -O3 -std=c++11 read_data.cpp -o read_data

内容的提问来源于stack exchange,提问作者Adam Ciesielski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 13:54:23