C++实现MATLAB矩阵计算程序

使用GCC C++代码,整合了MATIO库、Eigen矩阵计算、多线程处理及日志系统,支持解析M代码中的矩阵运算并输出多种格式结果:

cpp 复制代码
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <regex>
#include <thread>
#include <future>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <filesystem>
#include <sstream>
#include <cctype>
#include <stdexcept>
#include <chrono>
#include <<iomanip>
#include <backward.hpp>
#include <matio.h>
#include <Eigen/Dense>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>

namespace fs = std::filesystem;
namespace backward { backward::SignalHandling sh; }

using Eigen::MatrixXd;
using Eigen::VectorXd;
using EigenMap = std::map<std::string, MatrixXd>;

// 全局日志器初始化
std::shared_ptr<spdlog::logger> init_logger() {
   auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
   console_sink->set_pattern("%^[%Y-%m-%d %H:%M:%S.%e] [%l]%$ %v");
   
   auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("matrix_calc.log", true);
   file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
   
   std::vector<spdlog::sink_ptr> sinks{console_sink, file_sink};
   auto logger = std::make_shared<spdlog::logger>("mat_exec", sinks.begin(), sinks.end());
   logger->set_level(spdlog::level::debug);
   spdlog::register_logger(logger);
   return logger;
}

auto logger = init_logger();

// 异常处理宏定义
#define THROW_EXCEPTION(msg) \
   do { \
       std::stringstream ss; \
       ss << msg << " [文件: " << __FILE__ << " 行: " << __LINE__ << "]"; \
       logger->error(ss.str()); \
       throw std::runtime_error(ss.str()); \
   } while(0)

// 矩阵运算工具类
namespace MatrixOps {
   MatrixXd add(const MatrixXd& a, const MatrixXd& b) {
       if (a.rows() != b.rows() || a.cols() != b.cols())
           THROW_EXCEPTION("矩阵加法维度不匹配");
       return a + b;
   }

   MatrixXd multiply(const MatrixXd& a, const MatrixXd& b) {
       if (a.cols() != b.rows())
           THROW_EXCEPTION("矩阵乘法维度不匹配");
       return a * b;
   }

   MatrixXd inv(const MatrixXd& m) {
       if (m.rows() != m.cols()) THROW_EXCEPTION("求逆要求方阵");
       return m.inverse();
   }

   MatrixXd pinv(const MatrixXd& m, double tol = 1e-6) {
       Eigen::JacobiSVD<MatrixXd> svd(m, Eigen::ComputeThinU | Eigen::ComputeThinV);
       VectorXd sv = svd.singularValues();
       MatrixXd S_inv = MatrixXd::Zero(m.cols(), m.rows());
       for (int i = 0; i < sv.size(); ++i) {
           if (sv(i) > tol) S_inv(i, i) = 1.0 / sv(i);
       }
       return svd.matrixV() * S_inv * svd.matrixU().transpose();
   }

   MatrixXd eig(const MatrixXd& m) {
       if (m.rows() != m.cols()) THROW_EXCEPTION("特征值要求方阵");
       Eigen::EigenSolver<MatrixXd> solver(m);
       return solver.eigenvalues().real();
   }

   MatrixXd svd(const MatrixXd& m) {
       Eigen::JacobiSVD<MatrixXd> svd(m, Eigen::ComputeThinU | Eigen::ComputeThinV);
       return svd.singularValues();
   }

   MatrixXd chol(const MatrixXd& m) {
       if (m.rows() != m.cols()) THROW_EXCEPTION("Cholesky分解要求方阵");
       Eigen::LLT<MatrixXd> llt(m);
       if (llt.info() == Eigen::NumericalIssue)
           THROW_EXCEPTION("矩阵非正定,无法Cholesky分解");
       return llt.matrixL();
   }
}

// MAT文件处理类
class MatFileHandler {
public:
   static EigenMap load(const std::string& path) {
       if (!fs::exists(path)) THROW_EXCEPTION("文件不存在: " + path);
       
       mat_t* mat = Mat_Open(path.c_str(), MAT_ACC_RDONLY);
       if (!mat) THROW_EXCEPTION("无法打开MAT文件: " + path);
       
       EigenMap variables;
       matvar_t* var;
       while ((var = Mat_VarReadNext(mat)) != nullptr) {
           if (var->data_type == MAT_T_DOUBLE && var->rank == 2) {
               MatrixXd matrix(var->dims[0], var->dims[1]);
               memcpy(matrix.data(), var->data, var->nbytes);
               variables[var->name] = matrix;
               logger->debug("加载变量: {} [{}x{}]", var->name, 
                            var->dims[0], var->dims[1]);
           }
           Mat_VarFree(var);
       }
       Mat_Close(mat);
       return variables;
   }

   static void save(const std::string& path, const EigenMap& variables) {
       mat_t* mat = Mat_CreateVer(path.c_str(), nullptr, MAT_FT_MAT5);
       if (!mat) THROW_EXCEPTION("无法创建MAT文件: " + path);
       
       for (const auto& [name, matrix] : variables) {
           size_t dims[2] = {
               static_cast<size_t>(matrix.rows()),
               static_cast<size_t>(matrix.cols())
           };
           matvar_t* var = Mat_VarCreate(
               name.c_str(), MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims,
               (void*)matrix.data(), MAT_F_DONT_COPY_DATA
           );
           if (!var || Mat_VarWrite(mat, var, MAT_COMPRESSION_NONE) != 0) {
               Mat_VarFree(var);
               Mat_Close(mat);
               THROW_EXCEPTION("写入变量失败: " + name);
           }
           Mat_VarFree(var);
           logger->debug("保存变量: {} [{}x{}]", name, dims[0], dims[1]);
       }
       Mat_Close(mat);
   }
};

// 其他格式输出类
class FormatExporter {
public:
   static void save_csv(const std::string& path, const EigenMap& variables) {
       std::ofstream file(path);
       if (!file.is_open()) THROW_EXCEPTION("无法创建CSV文件: " + path);
       
       for (const auto& [name, matrix] : variables) {
           file << "=== " << name << " (" << matrix.rows() << "x" << matrix.cols() << ") ===" << std::endl;
           for (int i = 0; i < matrix.rows(); ++i) {
               for (int j = 0; j < matrix.cols(); ++j) {
                   file << matrix(i, j);
                   if (j < matrix.cols() - 1) file << ",";
               }
               file << std::endl;
           }
           file << std::endl;
       }
   }

   // 可扩展实现Excel和Parquet格式
   static void save_excel(const std::string& path, const EigenMap& variables) {
       logger->warn("Excel格式暂未实现: {}", path);
   }

   static void save_parquet(const std::string& path, const EigenMap& variables) {
       logger->warn("Parquet格式暂未实现: {}", path);
   }
};

// M代码解析器
class MCodeParser {
public:
   explicit MCodeParser(const std::string& script_path) {
       parse_script(script_path);
   }

   void execute(EigenMap& variables) const {
       for (size_t i = 0; i < instructions.size(); ++i) {
           try {
               const auto& [lhs, rhs] = instructions[i];
               MatrixXd result = evaluate_expression(rhs, variables);
               variables[lhs] = result;
               logger->info("执行成功: {} = [{}x{}矩阵] (行 {})", 
                            lhs, result.rows(), result.cols(), i + 1);
           } catch (const std::exception& e) {
               logger->error("执行失败 (行 {}): {}\n{}", 
                            i + 1, instructions[i].first + " = " + instructions[i].second, e.what());
           }
       }
   }

private:
   std::vector<std::pair<std::string, std::string>> instructions;

   void parse_script(const std::string& path) {
       std::ifstream file(path);
       if (!file.is_open()) THROW_EXCEPTION("无法打开M文件: " + path);
       
       std::string line;
       int line_num = 0;
       while (std::getline(file, line)) {
           line_num++;
           // 移除注释和空白
           size_t comment_pos = line.find('%');
           if (comment_pos != std::string::npos) {
               line = line.substr(0, comment_pos);
           }
           line = trim(line);
           if (line.empty()) continue;

           // 解析赋值语句
           size_t eq_pos = line.find('=');
           if (eq_pos == std::string::npos) {
               logger->warn("忽略无效语句 (行 {}): {}", line_num, line);
               continue;
           }

           std::string lhs = trim(line.substr(0, eq_pos));
           std::string rhs = trim(line.substr(eq_pos + 1));
           if (lhs.empty() || rhs.empty()) {
               logger->warn("忽略不完整赋值 (行 {}): {}", line_num, line);
               continue;
           }

           instructions.emplace_back(lhs, rhs);
       }
       logger->info("解析完成: {} 条有效指令", instructions.size());
   }

   static std::string trim(const std::string& s) {
       auto start = std::find_if_not(s.begin(), s.end(), ::isspace);
       auto end = std::find_if_not(s.rbegin(), s.rend(), ::isspace).base();
       return (start < end) ? std::string(start, end) : "";
   }

   MatrixXd evaluate_expression(const std::string& expr, const EigenMap& vars) const {
       // 处理函数调用
       static std::regex func_re(R"((\w+)\s*\(\s*([^)]*)\s*\))");
       std::smatch match;
       if (std::regex_match(expr, match, func_re)) {
           std::string func = match[1];
           std::vector<std::string> args = split_arguments(match[2]);
           return call_function(func, args, vars);
       }

       // 处理二元运算
       static std::vector<std::pair<std::regex, std::function<MatrixXd(const MatrixXd&, const MatrixXd&)>>> ops = {
           {std::regex(R"(^\s*(\w+)\s*\+\s*(\w+)\s*$)"), MatrixOps::add},
           {std::regex(R"(^\s*(\w+)\s*\*\s*(\w+)\s*$)"), MatrixOps::multiply}
       };

       for (const auto& [op_re, op_func] : ops) {
           if (std::regex_match(expr, match, op_re)) {
               std::string a_name = match[1];
               std::string b_name = match[2];
               
               if (!vars.count(a_name)) THROW_EXCEPTION("未定义变量: " + a_name);
               if (!vars.count(b_name)) THROW_EXCEPTION("未定义变量: " + b_name);
               
               return op_func(vars.at(a_name), vars.at(b_name));
           }
       }

       // 处理变量引用
       if (vars.count(expr)) {
           return vars.at(expr);
       }

       THROW_EXCEPTION("无法解析表达式: " + expr);
   }

   static std::vector<std::string> split_arguments(const std::string& args_str) {
       std::vector<std::string> args;
       std::istringstream iss(args_str);
       std::string arg;
       while (std::getline(iss, arg, ',')) {
           arg = trim(arg);
           if (!arg.empty()) args.push_back(arg);
       }
       return args;
   }

   MatrixXd call_function(const std::string& name, const std::vector<std::string>& args, const EigenMap& vars) const {
       if (args.empty()) THROW_EXCEPTION("函数调用缺少参数: " + name);
       
       // 检查所有参数是否存在
       for (const auto& arg : args) {
           if (!vars.count(arg)) THROW_EXCEPTION("函数参数未定义: " + arg);
       }

       // 调用对应矩阵函数
       if (name == "inv" && args.size() == 1) return MatrixOps::inv(vars.at(args[0]));
       if (name == "pinv" && args.size() == 1) return MatrixOps::pinv(vars.at(args[0]));
       if (name == "eig" && args.size() == 1) return MatrixOps::eig(vars.at(args[0]));
       if (name == "svd" && args.size() == 1) return MatrixOps::svd(vars.at(args[0]));
       if (name == "chol" && args.size() == 1) return MatrixOps::chol(vars.at(args[0]));
       
       THROW_EXCEPTION("不支持的函数或参数数量错误: " + name);
   }
};

// 线程池处理器
class ThreadPool {
public:
   explicit ThreadPool(size_t threads = std::thread::hardware_concurrency()) 
       : stop(false) {
       for (size_t i = 0; i < threads; ++i) {
           workers.emplace_back([this] {
               while (true) {
                   std::function<void()> task;
                   {
                       std::unique_lock<std::mutex> lock(queue_mutex);
                       condition.wait(lock, [this] { return stop || !tasks.empty(); });
                       if (stop && tasks.empty()) return;
                       task = std::move(tasks.front());
                       tasks.pop();
                   }
                   task();
               }
           });
       }
       logger->info("线程池初始化完成,线程数: {}", workers.size());
   }

   template<class F>
   void enqueue(F&& f) {
       {
           std::unique_lock<std::mutex> lock(queue_mutex);
           if (stop) throw std::runtime_error("线程池已停止,无法添加任务");
           tasks.emplace(std::forward<F>(f));
       }
       condition.notify_one();
   }

   ~ThreadPool() {
       {
           std::unique_lock<std::mutex> lock(queue_mutex);
           stop = true;
       }
       condition.notify_all();
       for (std::thread& worker : workers) {
           if (worker.joinable()) worker.join();
       }
   }

private:
   std::vector<std::thread> workers;
   std::queue<std::function<void()>> tasks;
   std::mutex queue_mutex;
   std::condition_variable condition;
   bool stop;
};

// 文件处理函数
void process_file(const std::string& mat_path, 
                const std::string& m_script,
                const std::vector<std::string>& output_formats) {
   try {
       logger->info("开始处理: {}", mat_path);
       
       // 加载MAT文件变量
       EigenMap variables = MatFileHandler::load(mat_path);
       logger->info("加载变量数量: {}", variables.size());
       
       // 解析并执行M代码
       MCodeParser parser(m_script);
       parser.execute(variables);
       
       // 生成输出文件名(移除原扩展名)
       std::string base_name = fs::path(mat_path).stem().string();
       
       // 保存结果到指定格式
       for (const auto& format : output_formats) {
           std::string output_path = base_name + "." + format;
           if (format == "mat") {
               MatFileHandler::save(output_path, variables);
           } else if (format == "csv") {
               FormatExporter::save_csv(output_path, variables);
           } else if (format == "xlsx") {
               FormatExporter::save_excel(output_path, variables);
           } else if (format == "parquet") {
               FormatExporter::save_parquet(output_path, variables);
           } else {
               logger->warn("不支持的输出格式: {}", format);
               continue;
           }
           logger->info("已保存到: {}", output_path);
       }
       
       logger->info("处理完成: {}", mat_path);
} catch (const std::exception& e) {
logger->error("处理文件失败: {}\n{}", mat_path, e.what());
}
}

// 目录处理函数
void process_directory(const std::string& dir_path,
const std::string& m_script,
const std::vectorstd::string& output_formats,
ThreadPool& pool) {
if (!fs::is_directory(dir_path)) {
logger->error("不是有效目录: {}", dir_path);
return;
}

for (const auto& entry : fs::directory_iterator(dir_path)) {
if (entry.is_regular_file() && entry.path().extension() == ".mat") {
pool.enqueue([=] {
process_file(entry.path().string(), m_script, output_formats);
});
}
}
logger->info("目录任务已全部加入队列: {}", dir_path);
}

// 命令行参数解析
struct CmdArgs {
std::string input_path;
std::string script_path;
std::vectorstd::string output_formats;
size_t threads = std::thread::hardware_concurrency();
};

CmdArgs parse_args(int argc, char* argv[]) {
if (argc < 4) {
std::cerr << "用法: " << argv[0]
<< " <输入路径(.mat或目录)> <M脚本路径> <输出格式(用逗号分隔)> [线程数]\n"
<< "示例: " << argv[0]
<< " data.mat calc.m mat,csv 4\n"
<< "支持格式: mat, csv, xlsx(待实现), parquet(待实现)\n";
exit(1);
}

CmdArgs args;
args.input_path = argv[1];
args.script_path = argv[2];

// 解析输出格式
std::istringstream iss(argv[3]);
std::string format;
while (std::getline(iss, format, ',')) {
if (!format.empty()) args.output_formats.push_back(format);
}

// 解析线程数
if (argc >= 5) {
try {
args.threads = std::stoul(argv[4]);
if (args.threads == 0) args.threads = 1;
} catch (...) {
logger->warn("无效线程数,使用默认值: {}", args.threads);
}
}

// 验证输入
if (!fs::exists(args.input_path)) {
THROW_EXCEPTION("输入路径不存在: " + args.input_path);
}
if (!fs::exists(args.script_path) || fs::path(args.script_path).extension() != ".m") {
THROW_EXCEPTION("无效M脚本文件: " + args.script_path);
}
if (args.output_formats.empty()) {
THROW_EXCEPTION("未指定输出格式");
}

return args;
}

int main(int argc, char* argv[]) {
try {
logger->info("程序启动");

// 解析命令行参数
CmdArgs args = parse_args(argc, argv);
logger->info("输入路径: {}", args.input_path);
logger->info("M脚本: {}", args.script_path);
logger->info("输出格式: {}", fmt::join(args.output_formats, ","));
logger->info("线程数: {}", args.threads);

// 初始化线程池
ThreadPool pool(args.threads);

// 处理输入(文件或目录)
if (fs::is_regular_file(args.input_path) &&
fs::path(args.input_path).extension() == ".mat") {
// 处理单个MAT文件
pool.enqueue([&] {
process_file(args.input_path, args.script_path, args.output_formats);
});
} else if (fs::is_directory(args.input_path)) {
// 处理目录下所有MAT文件
process_directory(args.input_path, args.script_path, args.output_formats, pool);
} else {
THROW_EXCEPTION("输入必须是.mat文件或目录: " + args.input_path);
}

// 等待所有任务完成(通过线程池析构实现)
logger->info("等待所有任务完成...");
} catch (const std::exception& e) {
logger->critical("程序异常终止: {}", e.what());
return 1;
}

logger->info("程序正常结束");
return 0;
}

代码说明

  1. 核心功能实现

    • 使用MATIO库完成.mat文件的读写操作
    • 基于Eigen库实现矩阵的基本运算(加、乘、逆、伪逆等)
    • 支持解析M代码中的简单赋值语句和函数调用
    • 通过线程池实现多文件并行处理
  2. 日志与异常处理

    • 集成spdlog实现带时间戳的日志记录(控制台+文件)
    • 详细的错误信息包含文件名、行号和调用堆栈
    • 异常处理覆盖文件操作、矩阵运算和参数解析等场景
  3. 扩展性设计

    • 采用模块化结构,便于添加新的矩阵运算函数
    • 预留ExcelParquet格式输出接口
    • 命令行参数支持灵活配置输入输出和线程数
  4. 使用方法

    • 编译时需链接matioeigenspdlog和线程库
    • 运行时通过命令行指定输入(文件/目录)、M脚本、输出格式和线程数
相关推荐
Ares-Wang4 分钟前
Node.js 》》bcryptjs 加密
开发语言·javascript·node.js
vvilkim19 分钟前
深入理解Java访问修饰符:封装的艺术
java·开发语言
最爱吃南瓜27 分钟前
JS逆向实战案例之----【通姆】252个webpack模块自吐
开发语言·javascript·爬虫·webpack·js逆向·算法模拟
夜斗小神社39 分钟前
【LeetCode 热题 100】(六)矩阵
算法·leetcode·矩阵
天地一流殇2 小时前
SimBA算法实现过程
深度学习·算法·对抗攻击·黑盒
hqxstudying2 小时前
java分布式定时任务
java·开发语言·分布式
2501_924730612 小时前
智慧城管复杂人流场景下识别准确率↑32%:陌讯多模态感知引擎实战解析
大数据·人工智能·算法·计算机视觉·目标跟踪·视觉检测·边缘计算
学不动CV了2 小时前
FreeRTOS入门知识(初识RTOS任务调度)(三)
c语言·arm开发·stm32·单片机·物联网·算法·51单片机
Kingfar_13 小时前
智能移动终端导航APP用户体验研究案例分享
人工智能·算法·人机交互·ux·用户界面·用户体验