Matlab API读取C++中的.mat文件,使用STL容器

6

我需要从C++读取一些.mat数据文件。我已经阅读了文档,但我希望知道如何以简洁优雅的方式处理数据,例如使用std:vector(适用于中等大小的.mat文件(10M〜1G),但应严肃考虑内存问题)。

我的函数大致如下:

#include <stdio.h>
#include "mat.h"
#include <vector>

int matread(const char *file, const vector<double>& pdata_v) {

MATFile *pmat;

pmat=matOpen("data.mat","r");
if (pmat == NULL) {
    printf("Error opening file %s\n", file);
    return(1);
}
mxArray *pdata = matGetVariable(pmat, "LocalDouble");

// pdata -> pdata_v

mxDestroy pa1;  // clean up
return 0;
}

那么问题是,我如何有效且安全地从 mxArray *pdata 数组复制到向量 pdata_v 中?


1
文件很大?内存可能是个问题?我们在谈论什么大小的文件?使用 fgets 并自己跟踪文件指针似乎是一个可行的方法。是的,这很困难,但不会给内存带来压力,而且速度相当快。 - Najzero
使用MATLAB中的fwrite函数并将数据以简单的二进制格式存储,这样C++就可以轻松读取(甚至是内存映射),比较容易实现。 - Ben Voigt
3个回答

10

下面是使用MAT-API的示例:

test_mat.cpp

#include "mat.h"
#include <iostream>
#include <vector>

void matread(const char *file, std::vector<double>& v)
{
    // open MAT-file
    MATFile *pmat = matOpen(file, "r");
    if (pmat == NULL) return;

    // extract the specified variable
    mxArray *arr = matGetVariable(pmat, "LocalDouble");
    if (arr != NULL && mxIsDouble(arr) && !mxIsEmpty(arr)) {
        // copy data
        mwSize num = mxGetNumberOfElements(arr);
        double *pr = mxGetPr(arr);
        if (pr != NULL) {
            v.reserve(num); //is faster than resize :-)
            v.assign(pr, pr+num);
        }
    }

    // cleanup
    mxDestroyArray(arr);
    matClose(pmat);
}

int main()
{
    std::vector<double> v;
    matread("data.mat", v);
    for (size_t i=0; i<v.size(); ++i)
        std::cout << v[i] << std::endl;
    return 0;
}

首先我们要构建独立程序,并创建一些MAT文件作为测试数据:

>> mex -client engine -largeArrayDims test_mat.cpp

>> LocalDouble = magic(4)
LocalDouble =
    16     2     3    13
     5    11    10     8
     9     7     6    12
     4    14    15     1

>> save data.mat LocalDouble

现在我们运行程序:

C:\> test_mat.exe
16 
5 
9 
4 
2 
11 
7 
14 
3 
10 
6 
15 
13 
8 
12 
1 

完全符合我的预期!只有一个问题,为什么要用matGetVariable而不是matGetNextVariable?如果从mat文件中读取多个向量,哪个更好? - lorniper
1
@lorniper:我想让这个例子保持简单。基本上,当你想通过名称检索特定变量时,可以使用matGetVariable。如果你想遍历MAT文件中保存的所有变量,请在循环内使用matGetNextVariable。查看随 MATLAB 一起提供的 matdgns.c 示例(在 $MATLABROOT/extern/examples/eng_mat/ 文件夹中查找)。 - Amro
@Amro,mat.h只能在Matlab本身中使用吗?我们不能在不同的IDE中使用它吗? - Sndn
Matlab API链接现在已经过时。"MAT Data API for C++"的当前URL是https://www.mathworks.com/help/matlab/matlab-data-array.html。 - Todd Gillette
1
@ToddGillette 不是完全正确的链接,我已经更新了帖子并放上了正确的链接。 - Amro
显示剩余3条评论

4

这里有另一个想法。如果你对C++代码中的裸指针过敏(顺便说一句,它们并没有什么问题),你可以使用boost或C++11的智能指针来包装裸指针,并使用一个deleter在指针超出作用域时调用正确的mxDestroyArray()函数进行自动释放。这样你就不需要进行复制操作,同时你的用户代码也不需要知道如何正确地释放内存。

typedef shared_ptr<mxArray> mxSmartPtr;

mxSmartPtr readMATarray(MATFile *pmat, const char *varname)
{
    mxSmartPtr pdata(matGetVariable(pmat, varname),
                     mxDestroyArray);  // set deleter
    return pdata;
}

int some_function() {
    mxSmartPtr pdata = readMATarray(pmat, "LocalDouble");
    ...
    // pdata goes out of scope, and mxDestroy automatically called
}

灵感来源于这里:http://www.boost.org/doc/libs/1_56_0/libs/smart_ptr/sp_techniques.html#incomplete


使用C++11的shared_ptr非常好,可以简化使用。我记下了! - chappjc

1
你可以首先获取 mxArray *pdata 的数据指针,然后将数据复制到 vector<double> pdata_v 中:
double *ptr = (double *) mxGetData(pdata);
pdata_v.resize(numOfData);
memcpy(&pdata_v[0], ptr, numOfData*sizeof(double));

提示1: 需要特别注意,在MATLAB中,矩阵是按列优先顺序排列的。因此,如果pdata存储[1 2 3; 4 5 6],则pdata_v将为1 4 2 5 3 6

提示2: 如果要更改其内容,请将const vector<double>& pdata_v更改为vector<double>& pdata_v


使用它是安全的,只要你正确地使用它,尽管它比你提到的C++风格更容易出错。 :) - herohuyongtao
你的解决方案编译成功,但是出现了“段错误11”的问题。我提到的方法在编译时会出现错误:“(std::vector<double>) (double*&, double*)”没有匹配项。 - lorniper
@lorniper 在此之前,请确保您已经为 vector<double> pdata_v 分配了内存,例如 pdata_v.resize(numOfData) - herohuyongtao
@lorniper 哎呀,已修复。 :P - herohuyongtao
1
@lorniper:如果你想使用那种语法,你可以调用vector::assign,如下所示:pdata_v.assign(ptr, ptr+num); 其中 double *ptr = mxGetPr(pdata);size_t num = mxGetNumberOfElements(pdata); - Amro
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接