使用C语言读取.mat文件:如何正确地读取单元结构。

13

我将尝试将Matlab代码翻译成C代码。这是我之前提问的延续。

在Matlab中,我使用了cell-structures,它包含了变量大小的矩阵(double)。以下是我的*.mat文件应该存储的玩具示例:

Matlab代码:

A = [[1 2 3]; [5 7 1]; [3 5 9]];
B = [[2 4];[5 7]];
Creator = 'DKumar';

nFilters = 2;

Filters{1} = [[-1.0 -1.0 -1.0]; [-1.0 8 -1.0]; [-1.0 -1.0 -1.0]];
Filters{2} = 2.0*[[-1.0 -1.0]; [-1.0 8]; [-1.0 -1.0]];

cd('/home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File');
save('Test_FILE.mat', 'A', 'B', 'Creator', 'nFilters', 'Filters');

C代码:函数“matread_Matrix”正确读取以*.mat格式存储的矩阵。但是,应该读取单元结构的函数“matread_Cell”无法正常工作。
#include <stdio.h>
#include <stdlib.h>
#include "/usr/local/MATLAB/R2011b/extern/include/mat.h"

mxArray *arr;
mxArray *C_CELL;
/* declare  a 2 x 1 array of pointers to access the cell array in C */
mxArray *cellArray[2];

struct stDoubleMat{
   double* pValueInField;
   int nRows, nCols;
};

void matread_Matrix(const char *file, const char *FieldName2Read, struct stDoubleMat* poDoubleMat_LOC)
{
    printf("Reading file %s...\n\n", file);

    //Open file to get directory
    MATFile* pmat = matOpen(file, "r");

    if (pmat == NULL) {
      printf("Error opening file %s\n", file);
      return;
    }

    // extract the specified variable
    arr = matGetVariable(pmat, FieldName2Read);

    double *pr;
    if (arr != NULL && !mxIsEmpty(arr)) {
        // copy data
        mwSize num = mxGetNumberOfElements(arr);

        pr = mxGetPr(arr);

        if (pr != NULL) {
        poDoubleMat_LOC->pValueInField = pr;
            poDoubleMat_LOC->nRows  = mxGetM(arr);
            poDoubleMat_LOC->nCols  = mxGetN(arr);
        }
    printf("matread_Matrix \n") ;
        printf( "oDoubleMat_LOC.nRows %i ; oDoubleMat_LOC.nCols %i \n", poDoubleMat_LOC->nRows , poDoubleMat_LOC->nCols);

    }else{
        printf("nothing to read \n") ;
    }


    // close the file
    matClose(pmat);

    return;
}

void matread_Cell(const char *file, const char *FieldName2Read, int CellIndex)
{
    printf("Reading file %s...\n\n", file);

    //Open file to get directory
    MATFile* pmat = matOpen(file, "r");

    if (pmat == NULL) {
      printf("Error opening file %s\n", file);
      return;
    }

    // extract the specified variable
    C_CELL = matGetVariable(pmat, FieldName2Read);
    cellArray[CellIndex] = mxGetCell(C_CELL, CellIndex);

    double* p2 = (double*)cellArray[CellIndex];
    int nRows  = mxGetM(cellArray[CellIndex]);
    int nCols  = mxGetN(cellArray[CellIndex]);

    printf(" From inside matread_Cell : nRows %i and nCols %i \n", nRows, nCols);

    int i2;
    for (i2 = 0; i2 < nRows*nCols; i2++)
    {
    printf(" copied value : %f \n", *p2);
    p2 = p2 +1;
    }

    // close the file
    matClose(pmat);
}


int main(int argc, char **argv)
{
    const char *FileName = "/home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File/Test_FILE.mat";
    const char *FieldName2Read = "A";

    struct stDoubleMat oDoubleMat; 
    matread_Matrix(FileName, FieldName2Read, &oDoubleMat);
    double* v = oDoubleMat.pValueInField;


    printf("From main \n");
    printf( "oDoubleMat.nRows %i ; oDoubleMat.nCols %i \n", oDoubleMat.nRows , oDoubleMat.nCols);

    int i;
    for (i = 0; i < oDoubleMat.nCols*oDoubleMat.nRows; i++)
    {
        printf(" copied value : %f \n", *v);
        v = v +1;
    }

    // Reading the structure
    const char *FieldName2Read2 = "Filters";
    matread_Cell(FileName, FieldName2Read2, 0);
    matread_Cell(FileName, FieldName2Read2, 1);


    // cleanup the mex-array
    mxDestroyArray(arr);
    mxDestroyArray(C_CELL);
    /* How to delete mxArray of pointer : should this be a array of pointers */
    //mxDestroyArray(cellArray[0]);
    //mxDestroyArray(cellArray[1]);

    return 0;
}

输出:

$ gcc -g -o Test Read_MatFile_DKU_2.c -I/usr/local/MATLAB/R2011b/extern/include -L/usr/local/MATLAB/R2011b/bin/glnxa64 -lmat -lmx

$ ./Test 
Reading file /home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File/Test_FILE.mat...

matread_Matrix 
oDoubleMat_LOC.nRows 3 ; oDoubleMat_LOC.nCols 3 
From main 
oDoubleMat.nRows 3 ; oDoubleMat.nCols 3 
 copied value : 1.000000 
 copied value : 5.000000 
 copied value : 3.000000 
 copied value : 2.000000 
 copied value : 7.000000 
 copied value : 5.000000 
 copied value : 3.000000 
 copied value : 1.000000 
 copied value : 9.000000 
Reading file /home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File/Test_FILE.mat...

 From inside matread_Cell : nRows 3 and nCols 3 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
Reading file /home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File/Test_FILE.mat...

 From inside matread_Cell : nRows 3 and nCols 2 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 

另外,我也无法正确读取这个字段:Creator = 'DKumar';

更新:

根据@Sherwin的建议:

我的C代码:

#include <stdio.h>
#include <stdlib.h>
#include "/usr/local/MATLAB/R2011b/extern/include/mat.h"

mxArray *arr;
mxArray *C_CELL;
/* declare  a 2 x 1 array of pointers to access the cell array in C */
mxArray *cellArray[2];

struct stDoubleMat{
   double* pValueInField;
   int nRows, nCols;
};


void matread_Matrix(MATFile* pmat , const char *FieldName2Read, struct stDoubleMat* poDoubleMat_LOC)
{
    // extract the specified variable
    arr = matGetVariable(pmat, FieldName2Read);

    double *pr;
    if (arr != NULL && !mxIsEmpty(arr)) {
        // copy data
        mwSize num = mxGetNumberOfElements(arr);

        pr = mxGetPr(arr);

        if (pr != NULL) {
        poDoubleMat_LOC->pValueInField = pr;
            poDoubleMat_LOC->nRows  = mxGetM(arr);
            poDoubleMat_LOC->nCols  = mxGetN(arr);
        }
    printf("matread_Matrix \n") ;
        printf( "oDoubleMat_LOC.nRows %i ; oDoubleMat_LOC.nCols %i \n", poDoubleMat_LOC->nRows , poDoubleMat_LOC->nCols);

    }else{
        printf("nothing to read \n") ;
    }
    return;
}

void matread_String(MATFile* pmat , const char *FieldName2Read)
{
    // extract the specified variable
    arr = matGetVariable(pmat, FieldName2Read);

    double *pr;
    if (arr != NULL && !mxIsEmpty(arr)) {
        // copy data
        mwSize num = mxGetNumberOfElements(arr);
        pr = mxGetPr(arr);

        if (pr != NULL) {
            char *p2 = (char*) pr; 

            // Printing and checking
        int i2;
        for (i2 = 0; i2 < num; i2++)
        {
        printf(" copied value : %s \n", p2);
        p2 = p2 +1;
        }

    }

    }else{
            printf("nothing to read \n") ;
    }
    return;
}

void matread_Cell(MATFile* pmat , const char *FieldName2Read, int CellIndex)
{

    // extract the specified variable
    C_CELL = matGetVariable(pmat, FieldName2Read);
    cellArray[CellIndex] = mxGetCell(C_CELL, CellIndex);

    double *p2 = (double*) mxGetPr(cellArray[CellIndex]); 
    int nRows  = mxGetM(cellArray[CellIndex]);
    int nCols  = mxGetN(cellArray[CellIndex]);

    printf(" From inside matread_Cell : nRows %i and nCols %i \n", nRows, nCols);

    int i2;
    for (i2 = 0; i2 < nRows*nCols; i2++)
    {
    printf(" copied value : %f \n", *p2);
    p2 = p2 +1;
    }
}


int main(int argc, char **argv)
{
    const char *FileName = "/home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File/Test_FILE.mat";
    const char *FieldName2Read = "A";

    //Open file to get directory
    printf("Reading file %s...\n\n", FileName);
    MATFile* pmat = matOpen(FileName, "r");

    if (pmat == NULL) {
      printf("Error opening file %s\n", FileName);
      return;
    }

    struct stDoubleMat oDoubleMat; 
    matread_Matrix(pmat, FieldName2Read, &oDoubleMat);
    double* v = oDoubleMat.pValueInField;

    int i;
    for (i = 0; i < oDoubleMat.nCols*oDoubleMat.nRows; i++)
    {
        printf(" copied value : %f \n", *v);
        v = v +1;
    }

    // Reading the structure
    const char *FieldName2Read2 = "Filters";
    matread_Cell(pmat, FieldName2Read2, 0);
    matread_Cell(pmat, FieldName2Read2, 1);

    // Reading the string
    const char *FieldName2Read3 = "Creator";
    matread_String(pmat, FieldName2Read3);

    // cleanup the mex-array
    mxDestroyArray(arr);
    mxDestroyArray(C_CELL);

    /* How to delete mxArray of pointer : should this be a array of pointers */
    //mxDestroyArray(cellArray[0]);
    //mxDestroyArray(cellArray[1]);


    // close the file
    matClose(pmat);

    return 0;
}

输出:

oDoubleMat.nRows 3 ; oDoubleMat.nCols 3 
 copied value : 1.000000 
 copied value : 5.000000 
 copied value : 3.000000 
 copied value : 2.000000 
 copied value : 7.000000 
 copied value : 5.000000 
 copied value : 3.000000 
 copied value : 1.000000 
 copied value : 9.000000 
 From inside matread_Cell : nRows 3 and nCols 3 
 copied value : -1.000000 
 copied value : -1.000000 
 copied value : -1.000000 
 copied value : -1.000000 
 copied value : 8.000000 
 copied value : -1.000000 
 copied value : -1.000000 
 copied value : -1.000000 
 copied value : -1.000000 
 From inside matread_Cell : nRows 3 and nCols 2 
 copied value : -2.000000 
 copied value : -2.000000 
 copied value : -2.000000 
 copied value : -2.000000 
 copied value : 16.000000 
 copied value : -2.000000 
 copied value : D 
 copied value :  
 copied value : K 
 copied value :  
 copied value : u 
 copied value :  
 copied value :  

问题:1)存储在creator中的字符串值未被正确显示。 2)如何删除cellArray[2]?

如果你在Matlab之外读取数据,为什么要将数据保存为MAT格式?为什么不将数据(在Matlab中)导出为hdf5格式,并使用现有的工具来读取hdf5二进制数据呢? - Shai
我以前从未使用过hdf5格式,也不知道它是否能够存储和访问单元结构。 - Garima Singh
2
Matlab将其MAT文件保存为hdf5格式(从版本7.3开始,据我所知)。因此,原则上它可以做任何你需要的事情。你必须动手学习如何与hdf5进行接口交互,但考虑到你正在经历与MAT格式相同(痛苦的)过程,我认为你最好学习一个更多功能的工具(HDF5),而不是专注于非常受限制的MAT...但这只是我的意见。 - Shai
请查看hdf5网页获取帮助和教程。 - Shai
这可能不是对我最优的解决方案。我主要使用Matlab编程,只有一个任务需要显著加速数据处理。幸运的是,有人已经编写了一个C代码求解器,声称至少快10倍。因此,我需要快速在Matlab和C之间进行接口,以检查他的说法。 - Garima Singh
1个回答

6

通过稍微修改,您的代码可以正常工作:

在函数"void matread_Cell"中,将行double* p2 = (double*)cellArray[CellIndex];替换为:

p2 = (double*) mxGetPr(cellArray[CellIndex]); 

我已经查看了它,它能够完成工作。
另外,要读取创建者字段,类似于 mtread_matrix 的代码应该可以使用,只是类型为 char* 而不是 double*(我没有检查这个,请让我知道是否有效)。 更新: 您可以使用以下代码来读取字符串。 (参考:这里
void matread_string(const char *file, const char *FieldName2Read, char *pr, mwSize *len)
{
    printf("Reading file %s...\n\n", file);

    //Open file to get directory
    MATFile* pmat = matOpen(file, "r");

    if (pmat == NULL) {
      printf("Error opening file %s\n", file);
      return;
    }

    // extract the specified variable
    arr = matGetVariable(pmat, FieldName2Read);

    if (arr != NULL && !mxIsEmpty(arr)) {
        // copy data
        mwSize num = mxGetNumberOfElements(arr);

        //int mxGetString(const mxArray *pm, char *str, mwSize strlen);
        int res= mxGetString(arr, pr, num+1); //strlen should be len+1. c.f. reference.
        if(res==0)
            printf("success!\n");
        else
            printf("failed.\n");


        if ( pr == NULL){
            printf("null pointer.\n");
        }   
        printf("matread_string \n") ;
        printf( "len: %i \n", (int)num);

        *len=num; 

    }else{
        printf("nothing to read \n") ;
    }
    // close the file
    matClose(pmat);

    return;
}

main函数中,您可以像这样使用它:
 const char *FieldName2Read3 = "Creator";
    char pr[20];
    mwSize len;
    matread_string(FileName, FieldName2Read3, pr, &len);

    //int i;
    printf(" copied value: %s \n",pr);
    for (i = 0; (mwSize) i <  len; i++)
    {
        printf(" copied value : %c \n", pr[i]);
    } 

关于释放cellArray,我收到了错误信息:“指针被释放但未分配”,因此我认为您不需要释放它。另一个有用的释放动态内存的命令是:void mxFree(void *ptr); 关于mexPrintf函数,我实际上能够使用它。我只是收到一个警告:implicit declaration of function 'mexPrintf' is invalid in C99 [-Wimplicit-function-declaration],因为我是通过gcc编译而不是mex。如果您正在使用gcc,则可能需要包含适当的库以识别该函数。您可能会发现这个链接对您有用,因为它对我起作用了。

我根据您的建议更新了我的代码,现在可以读取CellArray,但仍然无法读取字符串。此外,我不知道如何删除cellArray [2]。另外,我更喜欢使用mexPrintf。但是,我遇到了一个错误:undefined reference to `mexPrintf'。有什么猜测吗? - Garima Singh

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