首頁 > 軟體

使用C/C++讀寫.mat檔案的方法詳解

2022-03-08 13:03:48

最近需要使用C++來處理matlab生成的資料, 參考了網上一些部落格,不過他們都是使用的VS,我比較喜歡使用Clion, 在設定的過程中也遇到了一些坑,記錄一下。

一、建立工程並新增測試程式碼

建立工程就不說了,注意一下我使用的編譯工具鏈是MinGW。測試程式碼參考的matlab官方的程式:讀取用 C/C++ 編寫的 MAT 檔案 - MATLAB & Simulink - MathWorks 中國,對官方的程式碼進行了小小的調整。

將程式中的path替換為你的mat檔案所在完整地址即可。

#include <cstdio>
#include "mat.h"
const char *path = "D:\Codes\MATLAB\test.mat";
int diagnose(const char *file) {
    MATFile *pmat;
    const char **dir;
    const char *name;
    int ndir;
    int i;
    mxArray *pa;
    printf("Reading file %s...nn", file);
    /*
     * Open file to get directory
     */
    pmat = matOpen(file, "r");
    if (pmat == NULL) {
        printf("Error opening file %sn", file);
        return (1);
    }
    /*
     * get directory of MAT-file
     */
    dir = (const char **) matGetDir(pmat, &ndir);
    if (dir == NULL) {
        printf("Error reading directory of file %sn", file);
        return (1);
    } else {
        printf("Directory of %s:n", file);
        for (i = 0; i < ndir; i++)
            printf("%sn", dir[i]);
    }
    mxFree(dir);
    /* In order to use matGetNextXXX correctly, reopen file to read in headers. */
    if (matClose(pmat) != 0) {
        printf("Error closing file %sn", file);
        return (1);
    }
    pmat = matOpen(file, "r");
    if (pmat == NULL) {
        printf("Error reopening file %sn", file);
        return (1);
    }
    /* Get headers of all variables */
    printf("nExamining the header for each variable:n");
    for (i = 0; i < ndir; i++) {
        pa = matGetNextVariableInfo(pmat, &name);
        if (pa == NULL) {
            printf("Error reading in file %sn", file);
            return (1);
        }
        /* Diagnose header pa */
        printf("According to its header, array %s has %d dimensionsn",
               name, mxGetNumberOfDimensions(pa));
        if (mxIsFromGlobalWS(pa))
            printf("  and was a global variable when savedn");
        else
            printf("  and was a local variable when savedn");
        mxDestroyArray(pa);
    }
    /* Reopen file to read in actual arrays. */
    if (matClose(pmat) != 0) {
        printf("Error closing file %sn", file);
        return (1);
    }
    pmat = matOpen(file, "r");
    if (pmat == NULL) {
        printf("Error reopening file %sn", file);
        return (1);
    }
    /* Read in each array. */
    printf("nReading in the actual array contents:n");
    for (i = 0; i < ndir; i++) {
        pa = matGetNextVariable(pmat, &name);
        if (pa == NULL) {
            printf("Error reading in file %sn", file);
            return (1);
        }
        /*
         * Diagnose array pa
         */
        printf("According to its contents, array %s has %d dimensionsn",
               name, mxGetNumberOfDimensions(pa));
        if (mxIsFromGlobalWS(pa))
            printf("  and was a global variable when savedn");
        else
            printf("  and was a local variable when savedn");
        mxDestroyArray(pa);
    }
    if (matClose(pmat) != 0) {
        printf("Error closing file %sn", file);
        return (1);
    }
    printf("Donen");
    return (0);
}
int main() {
    int result;
    result = diagnose(path);
    if (!result) {
        printf("SUCCESS!n");
    } else {
        printf("FALURE!n");
    }
    return 0;
}

二、修改CmakeLists檔案

設定包含路徑(相當於VS中的新增附加包含目錄):

set(INC_DIR1 E:\MATLAB\R2019b\extern\include)
set(INC_DIR2 E:\MATLAB\R2019b\extern\include\win64)
# head file path,標頭檔案目錄
include_directories(${INC_DIR1}) # 指定標頭檔案的搜尋路徑,相當於指定 gcc 的 - I 引數
include_directories(${INC_DIR2}) # 指定標頭檔案的搜尋路徑,相當於指定 gcc 的 - I 引數

設定庫目錄(相當於VS中的新增附加庫目錄),以及需要包含的庫(相當於VS中的新增附加依賴庫):

set(LINK_DIR E:\MATLAB\R2019b\extern\lib\win64\mingw64
link_directories(${LINK_DIR}) # 動態連結庫或靜態連結庫的搜尋路徑,相當於 gcc 的 - L 引數
link_libraries(libmat libmx libmex libeng) # All targets link with the same set of libs

下面是我的CmakeLists檔案的完整內容:

cmake_minimum_required(VERSION 3.21)
# project name,指定專案的名稱,一般和專案的資料夾名稱對應
project(read_mat)
# 設定引數
set(CMAKE_CXX_STANDARD 14)
set(INC_DIR1 E:\MATLAB\R2019b\extern\include)
set(INC_DIR2 E:\MATLAB\R2019b\extern\include\win64)
set(LINK_DIR E:\MATLAB\R2019b\extern\lib\win64\mingw64)
# head file path,標頭檔案目錄
include_directories(${INC_DIR1}) # 指定標頭檔案的搜尋路徑,相當於指定 gcc 的 - I 引數
include_directories(${INC_DIR2}) # 指定標頭檔案的搜尋路徑,相當於指定 gcc 的 - I 引數
link_directories(${LINK_DIR}) # 動態連結庫或靜態連結庫的搜尋路徑,相當於 gcc 的 - L 引數
link_libraries(libmat libmx libmex libeng) # All targets link with the same set of libs
cmake_minimum_required(VERSION 3.21)
add_executable(read_mat main.cpp)

三、新增環境變數

新增下面的環境變數,注意替換matlab安裝地址。

E:MATLABR2019bbinwin64
E:MATLABR2019bexternlibwin64mingw64

記得新增完環境變數後重啟一下電腦。

重啟完成後,對於部分人來說到這裡整個設定就完成了,就能夠執行程式了。但是部分人比如說我自己,在執行的時候出現下面的錯誤:

Process finished with exit code -1073741515 (0xC0000135)

如果遇到這樣的錯誤,請繼續往下面看。

四、令人頭禿的錯誤

對於上面的錯誤,我嘗試了在網上搜尋,可是沒有找到解決的辦法。然後我就使用VS設定了一遍,程式執行還是失敗了,提示遇到了如下的錯誤:

無法定位程式輸入點H5Rdereference於動態連結庫libmat.dll上

出現這種問題的原因是dll動態庫發生了衝突,我是因為新增了Anaconda的環境變數,在Anaconda中也有hdf5.dll檔案,程式首先定位到了Anaconda的dll檔案,而不是matlab的dll檔案,解決衝突的辦法就是將Anacomda的環境變數移動到matlab環境變數之後即可。

記得修改完環境變數之後重啟一下電腦。

五、執行結果

解決完前面遇到的問題後,再次執行程式,可以看到成功了,程式輸出結果如下:

Directory of D:CodesMATLABDP-TBDmatlab_codetest.mat:
matrix

Examining the header for each variable:
According to its header, array matrix has 2 dimensions
  and was a local variable when saved

Reading in the actual array contents:
According to its contents, array matrix has 2 dimensions
  and was a local variable when saved
Done
SUCCESS!

Process finished with exit code 0

總結

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!  


IT145.com E-mail:sddin#qq.com