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

求助:如何在C++中不使用外部库或API读取.mat文件?

不用外部库在C++中读取.mat文件的方法

你的代码直接通过seekg(2665452)定位读取img1的方式是错误的——.mat文件不是简单的二进制数据拼接,它遵循Level 5 MAT-file格式规范,包含文件头、变量元数据(类型、尺寸、名称)、数据块等结构,必须按格式解析才能正确读取内容。

下面是手动解析的核心步骤和代码示例:

1. 读取并验证文件头

.mat文件开头128字节是固定格式的文件头,包含版本、字节序等关键信息:

#include <fstream>
#include <cstdint>
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>

// 字节交换工具函数
uint32_t swap32(uint32_t val) {
    return ((val >> 24) & 0xFF) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | ((val << 24) & 0xFF000000);
}

double swap64(double val) {
    uint64_t tmp = *reinterpret_cast<uint64_t*>(&val);
    tmp = ((tmp >> 56) & 0xFF) | ((tmp >> 40) & 0xFF00) | ((tmp >> 24) & 0xFF0000) | ((tmp >> 8) & 0xFF000000) |
          ((tmp << 8) & 0xFF00000000) | ((tmp << 24) & 0xFF0000000000) | ((tmp << 40) & 0xFF000000000000) | ((tmp << 56) & 0xFF00000000000000);
    return *reinterpret_cast<double*>(&tmp);
}

struct MatHeader {
    char magic[4];       // 固定为"MATL"
    uint16_t version;    // Level 5版本为0x0100
    char endian[2];      // "IM"表示小端字节序,"MI"表示大端
    char reserved[120];  // 预留空间,无需处理
};

int main() {
    std::ifstream file("file.mat", std::ios::binary);
    if (!file.is_open()) {
        std::cerr << "文件打开失败" << std::endl;
        return 1;
    }

    // 读取文件头
    MatHeader header;
    file.read(reinterpret_cast<char*>(&header), sizeof(MatHeader));

    // 验证文件格式
    if (std::string(header.magic, 4) != "MATL" || header.version != 0x0100) {
        std::cerr << "不是Level 5格式的.mat文件" << std::endl;
        return 1;
    }

    // 判断是否需要字节交换(如果文件字节序和当前系统不一致)
    bool need_swap = (std::string(header.endian, 2) == "MI");

2. 循环解析变量数据块

每个变量都以8字节的标签开头(前4字节是数据类型,后4字节是数据长度),我们需要循环读取标签,筛选出目标变量:

struct MatTag {
        uint32_t type;
        uint32_t length;
    };

    MatTag tag;
    while (file.read(reinterpret_cast<char*>(&tag), sizeof(tag))) {
        // 处理字节序
        if (need_swap) {
            tag.type = swap32(tag.type);
            tag.length = swap32(tag.length);
        }

        switch (tag.type) {
            case 0x00000007: { // miDOUBLE类型,对应你的img1、img2
                // 读取变量名(miUTF8类型标签)
                MatTag name_tag;
                file.read(reinterpret_cast<char*>(&name_tag), sizeof(name_tag));
                if (need_swap) {
                    name_tag.type = swap32(name_tag.type);
                    name_tag.length = swap32(name_tag.length);
                }
                if (name_tag.type != 0x0000001B) { // 确认是UTF8字符串
                    file.seekg(name_tag.length, std::ios::cur);
                    continue;
                }
                std::string var_name(name_tag.length, '\0');
                file.read(&var_name[0], name_tag.length);

                // 读取数组维度(miINT32类型,2维数组对应2个整数:行×列)
                MatTag dim_tag;
                file.read(reinterpret_cast<char*>(&dim_tag), sizeof(dim_tag));
                if (need_swap) {
                    dim_tag.type = swap32(dim_tag.type);
                    dim_tag.length = swap32(dim_tag.length);
                }
                if (dim_tag.type != 0x00000004) { // 确认是INT32类型
                    file.seekg(dim_tag.length, std::ios::cur);
                    continue;
                }
                uint32_t dims[2];
                file.read(reinterpret_cast<char*>(dims), dim_tag.length);
                if (need_swap) {
                    dims[0] = swap32(dims[0]);
                    dims[1] = swap32(dims[1]);
                }

                // 读取并处理目标变量数据
                size_t data_size = dims[0] * dims[1] * sizeof(double);
                if (var_name == "img1") {
                    cv::Mat img1(dims[0], dims[1], CV_64F);
                    file.read(reinterpret_cast<char*>(img1.data), data_size);

                    // 如果需要字节交换,逐个转换double值
                    if (need_swap) {
                        double* ptr = img1.ptr<double>();
                        for (size_t i = 0; i < dims[0]*dims[1]; ++i) {
                            ptr[i] = swap64(ptr[i]);
                        }
                    }

                    // 转换为8位图像并保存
                    cv::Mat img1P;
                    img1.convertTo(img1P, CV_8U);
                    cv::imwrite("img1P.png", img1P);
                } else if (var_name == "img2") {
                    // 按同样逻辑处理img2
                }
                break;
            }
            case 0x00000013: { // miSTRUCT类型,对应你的s结构体
                // 不需要解析结构体的话,直接跳过对应长度的字节
                file.seekg(tag.length, std::ios::cur);
                break;
            }
            default: {
                // 其他未知类型,直接跳过
                file.seekg(tag.length, std::ios::cur);
                break;
            }
        }
    }

    file.close();
    return 0;
}

关键注意事项

  • 不能直接按已知大小跳过数据,必须通过标签解析定位变量
  • 必须处理字节序问题,否则读取的double值会完全错乱
  • 结构体类型如果不需要解析,直接跳过其标签标记的长度即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 21:34:57