我几乎每天都会在 MATLAB 和 Excel 之间来回倒腾数据仿真结果要导出给不会写代码的同事实验记录表要读进来做后续分析。说实话“MATLAB 怎么把数据导入导出到 Excel”这个事看起来简单但翻过车的都知道坑基本都在细节里。同样是读一个文件有人一行代码拿到整整齐齐的表格有人却对着乱掉的列名和读成数字的日期发愁同样是写一个文件有人交出去就能直接透视有人却被“Excel 里怎么全是文本格式”搞得返工。这篇文章就把我这些年用下来的经验捋一遍。从函数选型、类型转换、日期序列号、缺失值处理到多 sheet 写入、追加数据、Windows 上用 COM 控制格式再到几个高频实战场景和性能心得一次性讲透。不管你是刚接触 MATLAB 的学生还是已经被 Excel 互通问题折磨过的打工人应该都能从中找到能直接照抄的写法。1. 动手之前先把 Excel 文件的“脾气”摸清楚在写任何一句读写代码之前先花十秒钟搞清楚你手里的 Excel 文件属于哪种类型、里面装着什么类型的数据。这个工作看起来多余实际上能帮你避开一大半的雷。1.1 xls 和 xlsx 的边界老格式的兼容问题比想象中多Excel 文件最常见的后缀是.xls和.xlsx两者底层结构完全不同。.xls是 Excel 97-2003 时代的二进制格式单表最多 65536 行、256 列.xlsx是后来的 OOXML 格式单表上限是 1048576 行、16384 列。MATLAB 的内建读取函数对.xlsx的支持最完整读取速度和稳定性都更好。而.xls老格式在部分 MATLAB 版本上仍然能读但偶尔会出现列类型推断异常、日期读取错乱、甚至直接报错的情况。我的建议很简单只要条件允许先把.xls文件另存为.xlsx再交给 MATLAB。尤其是那种从老旧设备或别人电脑上拷来的文件先另存一遍后面对话能顺畅很多。如果你没有权限改源文件再考虑用旧式读取函数或 COM 兜底。另外要注意.xlsx本质上是一个压缩包里面是若干 XML 文件。MATLAB 读取它时不需要安装 Excel但写入时对公式、格式这类高级特性的支持非常有限。如果文件里带了大量图表、条件格式、数据验证那内建读写函数基本只认“值”不认“皮”。这时候就得看下面的操作系统差异了。1.2 表格内容的结构直接决定你该选哪个函数Excel 单元格本身是有类型的数值、文本、日期、逻辑值、错误值。这些类型进入 MATLAB 之后会被映射成完全不同的数据类型。数值会变成 double文本默认变成 string 或 char日期可能变成 datetime也可能是 Excel 日期序列号错误值如#N/A通常变成 missing 或 NaN。假如一列里既有数字又有文字readtable会把整列推断为文本类型readmatrix遇到这种列会直接受影响因为它只认数值readcell则会把每个单元格原样保存下来由你后续处理。所以在选函数之前先看一眼表格第一行是不是表头有没有合并单元格日期列多不多有没有空值心里有个大概的谱再动手写代码。1.3 系统差异Windows 上才有“外挂”权限这里说的“外挂”是指 COM/ActiveX 接口。在 Windows 上MATLAB 可以通过actxserver(Excel.Application)直接驱动本机安装的 Excel 程序读写单元格、设置格式、执行公式全部可控。这个能力在 macOS 和 Linux 上是不存在的内建读写函数成了唯一选择。这个差异对你的项目设计影响很大。如果只是在实验室里自己分析数据内建函数足够如果要做成一个在客户服务器上跑的平台而对方机器上可能没装 Excel那必须老老实实用内建函数别指望 COM。还有一种情况是服务器上装了 Excel但运行服务的账户权限不足COM 依然会失败。这些坑我后面在第 5 章的实战场景里展开讲。2. 读入数据readtable、readmatrix、readcell 怎么选MATLAB 自 R2019a 起逐步推荐用readtable、readmatrix、readcell这一组函数替代老旧的xlsread。新函数不仅能读.xlsx还能读.csv、.txt等格式灵活度更高。下面按使用场景逐个说清楚。2.1 readtable遇到千奇百怪的表格它最抗造绝大多数普通表格文件我都会直接用readtable。它返回的是 table 类型每个变量能保留自己的数据类型适合后续做统计、画图、机器学习特征提取。最基本的用法T readtable(data.xlsx);读进来之后表头会默认变成变量名每列类型由内容自动推断。好用的点在于许多“不干净”的情况它都能容忍比如空单元格会变成 missing某些错误值会变成 NaN文本列和数值列混在一起也能各自安全保存。真正容易出问题的是表头。如果表头里有空格、括号、单位、重复名字MATLAB 默认会把变量名做规范化处理。比如温度 (℃)会变成形如温度_C__的东西或者变成Var1这种占位名导致后面T.温度这种访问方式直接失效。解决办法是读之前先指定保留原始变量名opts detectImportOptions(data.xlsx); opts.VariableNamingRule preserve; T readtable(data.xlsx, opts);另一个高频需求是跳过开头的注释行。很多实验数据文件前几行是项目编号、设备型号、测试人姓名从第 4 行才是真正的表头。可以用DataLines指定数据开始位置opts detectImportOptions(data.xlsx); opts.DataLines [4 inf]; % 从第 4 行开始读到文件末尾 T readtable(data.xlsx, opts);2.2 readmatrix数据干净时的最快路径如果你的 Excel 文件就是一块纯数值矩阵没有任何表头、没有文本列那readmatrix是最合适的选择。它直接返回 double 矩阵后续计算不用再做类型转换速度也比readtable快一截。A readmatrix(matrix.xlsx, Sheet, Sheet1, Range, A1:H200);一个常见误解是遇到文本单元格readmatrix会直接报错。实际上它默认会把这些位置变成 NaN并给出警告。如果这个文件里文本列只是少数装饰性内容这种表现反而方便。但如果一列全是文本那还是老老实实换用readcell或readtable。2.3 readcell把每个单元格都保下来readcell返回的是 cell 数组一格格对应原始 Excel 单元格。每个单元格里保存“原味”内容数值是 double文本是字符串日期是 datetime逻辑是 logical。它不会帮你做任何合并或推断适合那种“我就是要自己控制每个格子”的情况。C readcell(messy.xlsx, Range, A1:F50);这种模式适合临时检查或者当你需要按固定位置读取某些特殊内容时。代价是后续处理都要自己写循环代码量会上去。所以我通常只在表格结构不固定、没有统一表头的情况下才用readcell。2.4 用 detectImportOptions 精细控制每一列当默认推断不满足要求时detectImportOptions是唯一的正规入口。它能提前扫描文件告诉你 MATLAB 准备怎么解析每一列然后你可以逐项修改。opts detectImportOptions(data.xlsx); % 只看两列 opts.SelectedVariableNames {time, value}; % 把 value 列的缺失值填充为 0 opts setvaropts(opts, value, FillValue, 0); % 指定日期格式 opts setvaropts(opts, time, DatetimeFormat, yyyy-MM-dd HH:mm:ss); T readtable(data.xlsx, opts);这里有个很实用的经验如果文件特别大先运行一次detectImportOptions再带着 opts 运行readtable总时间往往比直接readtable一遍要短。因为第一次扫描得到的类型信息是确定的第二次读取时不需要再逐列猜类型少了很多分支判断。尤其是几千列、几万行的表选对列、选对行之后速度差异会非常明显。3. 导出数据writetable、writematrix 及它们的短板写入方向的任务同样由三个函数包揽writetable写表格、writematrix写矩阵、writecell写元胞数组。基本语法都很直白但几个隐蔽问题值得单独说。3.1 常规导出两种函数和几个常用参数writetable(T, result.xlsx, Sheet, 结果); writematrix(A, result.xlsx, Sheet, 数据); writecell(C, result.xlsx, Sheet, 备注);writetable会带上变量名作为第一行表头所以交出去的 Excel 天然可读。writematrix没有任何表头适合后续还要用其他程序接着处理的数据。默认情况下每次调用这些函数时如果指定文件已存在写入行为会覆盖目标 sheet 的内容。关于已有工作簿里其他 sheet 是否保留不同版本上行为有细微差别。我的习惯是写之前先删掉旧文件或者先写到临时文件名确认无误再替换if exist(result.xlsx, file) delete(result.xlsx); end writetable(T1, result.xlsx, Sheet, 第一组); writetable(T2, result.xlsx, Sheet, 第二组);这样能避免许多“到底覆盖了没有”“原来的表去哪了”的莫名其妙问题。3.2 日期列写出去变成了文本经典翻车现场这是我见过最多的返工原因。MATLAB 里的 datetime 列用writetable写入 Excel 时默认会被写成类似01-Jan-2023的文本字符串而不是 Excel 认识的日期数值。你打开 Excel 一看内容确实在但用日期筛选、透视、排序时全都不对因为它本质上是文本。如果下游使用者需要在 Excel 里做日期运算最简单的办法是在 MATLAB 里先把 datetime 转成 Excel 日期序列号写入后让 Excel 按数值处理。转换逻辑下一章会详细讲这里先说操作把 datetime 列转成 double 序列号替换原列后再写文件。如果连格式都希望直接是yyyy-mm-dd这种显示效果那就必须上 COM在写入后设置单元格的NumberFormat。3.3 想追加写入或者更新已有 sheet该怎么办writetable不支持“往已有表的末尾追加几行”这种操作。它要么创建新文件要么覆盖当前 sheet。一个常见的绕法是读旧数据、拼上新数据、再整体写回。文件不大时完全够用old readtable(log.xlsx, Sheet, 运行记录); new [old; newRows]; writetable(new, log.xlsx, Sheet, 运行记录);对于大文件或者不想反复读写的情况用writematrix和writecell的Range参数可以做到定点写入。先算出原表已经有多少行然后把新数据写到从下一行开始的区域。这里注意writetable没有Range参数想精确定位写入位置只能用writematrix、writecell或 COM。3.4 格式、公式、列宽内建导出确实管不了内建写入函数做的是“纯数据落盘”不具备把单元格加粗、设置列宽、填充颜色、写入公式这些能力。如果只是交一份原始数据完全没问题但如果你要生成一份可以直接发给客户或者领导的成品报表那内建函数就帮不上忙了。我现在的经验是能用内建函数就用内建函数格式需求简单时顺手在 Excel 里做模板把 MATLAB 结果往里填真正需要自动化出带格式的报表时Windows 环境下直接用 COM。下一章的类型互转和第 5 章的实战演示会把这两条路线都走一遍。4. 类型转换与数据清洗导入导出的最后一公里读进来的数据很少能直接拿来分析写出去的数据也很少能直接交付。中间这段“最后一公里”往往是整个流程里最花时间的地方尤其是日期和缺失值。4.1 Excel 日期序列号和 MATLAB 时间类型的互转Excel 内部把日期保存成一个数字从 1899 年 12 月 30 日算起的天数。为什么是这个日子涉及一段历史遗留问题包括 1900 年被错误地当作闰年、为了兼容更早的表格软件而保留的偏移等等。作为使用者只需要记住一个结论Excel 的日期序列号 1 对应 1900 年 1 月 1 日2 对应 1900 年 1 月 2 日以此类推。1900 年 3 月 1 日之后这个序列号与真实天数的对应关系就完全稳定了。MATLAB 的 datenum 是从公元 0 年 1 月 1 日起算的天数跟 Excel 不是同一个基准。如果直接把 Excel 序列号当 datenum 用得到的时间会偏得离谱。正确转换方式如下% Excel 序列号 - MATLAB datetime excelSerial 44927; % 这个数大概是 2023-01-01 附近 dt datetime(1899,12,30) days(excelSerial); % MATLAB datetime - Excel 序列号 dt datetime(2023,1,1); excelSerial days(dt - datetime(1899,12,30));这里用datetime(1899,12,30)作为基准直接跳过了 Excel 那个闰年 bug 的干扰。如果你处理的日期都在 1900 年 3 月之后这套转换没有任何问题如果涉及更早的日期那要专门考虑 1900-02-29 这个不存在的日期带来的 1 天偏移实务中极少遇到知道有这么回事就行。一个很常见的误操作是使用readmatrix读 Excel 里的日期列得到的就是一串 double 序列号。这时候如果你不知道它代表什么拿去做分析就是一堆看似乱七八糟的数字。readtable通常会聪明地把它转成 datetime但如果你想追求极致的读取速度而用了readmatrix就一定要记得手动转换或者提前在导入选项里把日期列处理成 datetime。4.2 空值、#N/A、NaN缺失值的三种形态怎么统一Excel 里的缺失值至少有三种长相纯粹的空单元格、函数返回的错误值#N/A、以及某些系统导出的字符串NaN。它们进入 MATLAB 后可能变成missing、NaN、或者文本NaN。不统一处理后续统计会出现各种诡异问题。优先推荐在读取阶段就把缺失值规则定好opts detectImportOptions(data.xlsx); opts setvaropts(opts, value, FillValue, NaN); T readtable(data.xlsx, opts);如果已经读进来了可以用ismissing统一检测missMask ismissing(T); T(missMask) 0; % 或者根据业务决定填充策略如果希望直接删掉含缺失值的行用rmmissing(T)最省事如果希望做插值可以按数值列用fillmissingT.value fillmissing(T.value, linear);注意fillmissing对 NaN 才生效对missing也能识别但它是按列操作的类型不对会直接报错。一个现实建议在读取阶段就把所有缺失值统一成 NaN 或统一填充成某个业务含义明确的值后面所有代码只用一套判断逻辑能省掉大量 debug 时间。4.3 混合类型表格转矩阵从 table 到 double 的常规路径读取完成后很多时候你需要把某些数值列变成 double 矩阵用于矩阵运算或机器学习。直接table2array(T)要求 table 里所有列类型一致否则报错。如果表格里混了文本列、日期列、数值列你需要先挑选子集numCols T{:, isvartype(T, numeric)};isvartype(T, numeric)返回一个逻辑向量标记哪些列是数值类型的然后一次性提取成矩阵。需要的话还可以把logical列、datetime列分别用isvartype提取。这样即使 table 里有几十列代码也只要两三行。还有一种情况readcell 读回来的元胞数组里数字是 double、文本是 string文本里可能还有123这种长得像数字的字符串。统一转数值时可以像下面这样逐格处理C readcell(mixed.xlsx); for i 1:numel(C) if isstring(C{i}) || ischar(C{i}) C{i} str2double(C{i}); end end A cell2mat(C);这段代码不够优雅但它足够稳。对于已经读成 string 的列也可以直接用str2double对整个列向量做转换转换失败的会变成 NaN。5. 三个高频实战场景的完整演示下面几个场景都是我实际处理过的典型需求。每个场景都给出完整思路和代码骨架你可以直接搬到自己的项目里改一改。5.1 场景一把实验记录表批量导入并自动清洗某次项目里收到一批实验记录表每个文件前 3 行是设备信息和操作员备注第 4 行才是真正的表头数据从第 5 行开始。里面有一列日期、两列数值、一列单位说明。文件有几十个分布在同一个文件夹里。逐个人工处理显然不现实于是写了个批处理脚本files dir(fullfile(data, *.xlsx)); allData []; for k 1:numel(files) filename fullfile(files(k).folder, files(k).name); opts detectImportOptions(filename); opts.DataLines [5 inf]; % 跳过前 4 行 opts.VariableNamingRule preserve; % 保留原始表头 opts.SelectedVariableNames {日期, 温度, 压力}; T readtable(filename, opts); % 日期列如果被读成 double 序列号转成 datetime if isnumeric(T.日期) T.日期 datetime(1899,12,30) days(T.日期); end % 缺失值统一按 0 处理 T.温度(ismissing(T.温度)) 0; T.压力(ismissing(T.压力)) 0; allData [allData; T]; %#okAGROW end这里有个小经验批量循环里不要用allData [allData; T]这种方式在数据量特别大的时候反复拼接那样会越来越慢。更好的做法是先计算出总行数或者用 cell 数组收集每个文件的结果最后再合并。几十个小文件这么拼没问题上万个文件就要注意了。5.2 场景二把 MATLAB 计算结果批量导出成多 sheet 工作簿另一个常见需求是把多组结果分别写到同一个 Excel 文件的不同 sheet 里方便查看和汇报。我的做法是先清理旧文件再按顺序写入if exist(report.xlsx, file) delete(report.xlsx); end % 第一张表总体概览 writetable(summaryTable, report.xlsx, Sheet, 概览); % 其后每一组结果一个 sheet for k 1:10 sheetName sprintf(第%d组, k); writetable(resultTables{k}, report.xlsx, Sheet, sheetName); end这种写法思路清晰但对于 sheet 顺序敏感的场景要注意每次调用writetable时新的 sheet 会加在文件末尾概览写在了第一个后面的组依次往后排符合预期。如果还想在最前面生成一张“目录表”就要在写入全部结果后重新排序内建函数做不到只能靠 COM 或者先用 Excel 处理。另一个细节是 sheet 命名。Excel 对 sheet 名有字符限制不能包含: \ / ? * [ ]这几种字符也不能过长。用中文命名没问题但如果你用文件名作为 sheet 名最好先做一次清洗把非法字符替换成下划线。5.3 场景三Windows 下用 COM 做格式级控制当需求变成“不仅要有数据还要表头加粗、列宽合适、日期列显示成 yyyy-mm-dd、冻结首行”时我一般直接上 COM。下面是一段最小可用的代码骨架e actxserver(Excel.Application); e.Visible 0; % 打开或新建工作簿 wb e.Workbooks.Open(fullfile(pwd, report.xlsx)); ws wb.Worksheets.Item(1); % 表头加粗 headerRange ws.Range(A1:F1); headerRange.Font.Bold true; % 设置列宽 ws.Columns.Item(1).ColumnWidth 12; ws.Columns.Item(2).ColumnWidth 20; % 日期列按 yyyy-mm-dd 显示 ws.Range(C2:C100).NumberFormat yyyy-mm-dd; % 冻结首行 ws.Activate; e.ActiveWindow.SplitRow 1; e.ActiveWindow.FreezePanes 1; wb.Save; wb.Close; e.Quit; delete(e);这段代码有几个值得注意的地方。第一务必在结束时调用e.Quit和delete(e)否则任务管理器里会残留 EXCEL.EXE 进程文件会被占住下次打开就会报“文件正被使用”。第二Range对象的Value属性返回的内容在 MATLAB 里通常是 cell 数组而且可能是m x 1或1 x n的形式需要的时候要 reshape 一下。第三COM 接口操作 Excel 每步都要经过进程间通信速度远低于内建函数只适合对格式有要求的中小规模报表。6. 处理大文件与疑难杂症的心得最后一章集中讲我在“大文件”和“疑难杂症”上攒下来的经验。这些内容不涉及太多新语法但能帮你在遇到类似问题时少走弯路。6.1 大表读写的提速办法options 缓存与分块处理对于几万行、几十列级别的表格readtable的表现一般还可以但如果你追求极致速度可以考虑三件事。第一先用detectImportOptions明确导入规则再带着规则表去读。第一次扫描有开销但第二次读取时少了类型推断整体反而更快。这个做法尤其适用于反复读取同一批结构的文件。第二只读需要的部分。通过SelectedVariableNames指定列、DataLines指定行能让 MATLAB 少做大量无意义的工作。如果文件有 100 列而你只需要 3 列把其他 97 列读进来纯属浪费内存和时间。第三如果文件大到一个读入就占几个 GB考虑分块。可以用Range参数按行窗口多次读取或者如果原文件是 CSV用datastore配合tall数组做流式处理。Excel 本身对单表行数有限制真到了百万行级别建议先把数据导成 CSV 文件再来处理效率会有质的提升。我实测过一个大概 8 万行、20 列的 xlsx 文件直接readtable需要三秒多先用detectImportOptions限制列数后变成一秒多换成readmatrix读纯数值部分不到一秒。对日常分析来说体验已经很不一样了。6.2 路径、占用、文件名这几个隐形坑最坑的一次经历是脚本反复运行某次突然报错说文件无法写入。查了半天原来是我自己上次用 Excel 打开过这个文件忘了关文件被进程锁住了。所以现在只要涉及写文件我的第一检查项就是有没有 Excel 窗口开着。中文路径在多数版本上没问题但一旦出现 COM 相关操作中文路径偶尔会出幺蛾子。排查思路很简单把文件复制到一个纯英文路径下再跑一遍。如果正常了说明是路径编码问题后续要么统一用英文路径要么用fullfile规范化路径再传。文件名本身也要谨慎。Excel 的 sheet 名不能包含非法字符而文件主名最好不要用纯数字开头也不要带特殊符号否则下游工具容易不认。代码里最好养成用fullfile或fileparts操作路径的习惯避免硬编码反斜杠和正斜杠造成的跨平台问题。6.3 一张选型速查表写给下次要用的你下面这张表是我自己一直用的选型参考。每次遇到“MATLAB 和 Excel 互通”类需求我都会先对照一遍需求推荐方案备注读取带表头、多类型数据readtable配合detectImportOptions处理不规则表头、指定数据行读取纯数值矩阵readmatrix速度快遇到文本会置为 NaN读取完全杂乱的单元格readcell保留每个单元格原始值后续自己处理写入多列表格writetable自动带变量名常用Sheet参数写入纯数值矩阵writematrix想要定位写可用Range参数追加数据到已有文件读后合并重写或者用writematrix/writecell配合Range控制格式、公式、列宽Windows 下用 COM速度慢、依赖本机 Excel适合报表跨平台、超大文件考虑 CSV 中转建议另开一个预处理步骤这张表不能替代理解原理但它能帮你在项目最忙乱的时候快速回到正轨。我最近一次用这张表就是帮一个关系不错的同事把一套月度报表流程自动化了。他原本每天要花二十分钟手工整理 Excel改完后脚本跑一趟格式、日期、多 sheet 全自动生成他只需要看一眼结果。整个脚本核心也就是readtable、writetable加一小段 COM 格式设置没有用到任何高深技巧但解决了实际问题。说实话MATLAB 和 Excel 的互通并不难难点全在“预期管理”你要知道哪些事情内建函数能做哪些事情必须借助 COM哪些问题来自历史遗留的日期规则哪些坑来自操作系统差异。把这些边界摸清楚了你写出来的读写代码就会越来越稳也越来越少需要回头修 bug。
阅读完成 · 觉得有帮助?