☰
MATLAB读取Excel不再碰运气:三函数选型与类型处理全攻略
2026/10/17 4:10:54 网站建设 项目流程

简介:面向MATLAB初学者与需要快速处理Excel数据的科研、工程人员,解决如何将工作表中的数据高效导入MATLAB工作区并供后续分析调用的问题。压缩包内共四个文件,包含可直接运行的主程序、示例数据表格、文本格式的使用说明以及文档资料,整体仅118KB,轻量精炼。主程序采用交互式运行模式,按提示操作即可查看读取结果,适合零基础入门与日常实验参考。已有1980人学习下载,具备一定参考价值。资源将主程序与示例数据分离,便于对照理解代码逻辑;通过跟随说明实践,读者能够掌握MATLAB读取Excel的常用函数与标准流程,并可将示例数据替换为自己的文件,灵活应用于数据处理和建模分析任务中。

1. 读Excel总是类型错乱?先分清“表结构三分类”再选MATLAB函数

我最早用MATLAB读Excel时,下意识就是一句xlsread,直到连续三次读同一份表格却分别拿到NaN、43000多和一堆乱码,才意识到问题根本不在函数,而在对数据形态的判断。同一份Excel里,纯数值、数值文本混合、日期缺席,处理方式完全不同。更麻烦的是,读进来的数据长什么样,取决于你调用的是xlsread、readtable还是readmatrix,它们背后对应的读取引擎和类型规则根本不一样。

这篇文章想解决的就是这件事:拿到一份Excel后怎么快速判断该用哪个读取函数,如何设置Sheet、Range、变量类型这些关键参数,以及遇到NaN、日期序列号、大文件卡死时怎么排查。核心目标是把MATLAB读取Excel从“碰运气”变成“按形状选函数”,让新老手都能少走弯路。

2. 三类读取函数怎么选:xlsread、readtable、readmatrix的适用边界与参数

2.1 xlsread:旧接口还在,但新代码不该再用

很多老代码里xlsread是绝对主力,它的标准调用方式是一次返回三个输出:

[data, text, raw] = xlsread('sales.xlsx', 'Sheet1', 'A1:D200');

data是所有数值组成的矩阵;text是文本内容构成的元胞数组;raw是原始单元格数据的完整元胞数组。当某一列全是文本时,data矩阵中这一列会被剔除,而不是补NaN;当整表只有一列文本时,data很可能是空数组。这个行为常让人困惑:明明表格有数值,data却空着。

从使用角度看,xlsread的问题在于它依赖Excel组件接口,在Linux服务器或未安装Excel的机器上经常直接报错,而且每次调用可能启动一个隐形的Excel进程,脚本结束后进程不一定会退出。新版MATLAB文档也不推荐在新代码里使用它。

我一般只在调试别人遗留的老脚本时才会保留xlsread,如果只是做数据读取,会毫不犹豫地换到下面两个函数。另外要注意xlsread的第三返回参数raw可以用来检查单元格真实内容,这对排查“为什么读出来不对”很有帮助,但不要依赖它做大范围数据读取,因为速度非常慢。

2.2 readtable:混合型表格的首选,自动类型推断的双刃剑

readtable是当前读取Excel最推荐的入口,它返回table类型,每一列有变量名,直接对接后续的数据清洗、绘图和统计分析。最小调用是:

T = readtable('sales.xlsx');

这段代码会自动读取第一个工作表的全部数据,并自动推断每列类型。如果某一列是纯数字,就会解析成double;如果一整列都是文本,就会解析成cellstr;如果数字和文本混在同一列,整列会被迫转成文本型元胞数组。这个“自动推断”看着方便,实际是把双刃剑:大部分情况省事,但遇到“大部分是数字,偶尔一个‘待确认’”时,整列会变成文本,后续求均值直接报错。

你可以用SelectedVariableNames只选需要的列,避免输入矩阵过大:

T = readtable('sales.xlsx', 'Sheet', 'Sheet1', 'Range', 'A1:D200', ... 'SelectedVariableNames', {'月份', '销售额'});

其中Sheet指定工作表名,Range使用Excel的A1风格区域,SelectedVariableNames在已经确定前几行是变量名时按变量名筛选列。这里的“变量名”实际是Excel首行单元格内容,如果首行有重复列名,MATLAB会自动改成脚本体面的名称,这个细节会在第5章展开。

另外readtable会自动把空数值单元格设为NaN,空文本单元格设为missing,这在多数情况下是正确的。如果你不希望它“聪明”,可以先读一个很小的范围,确认推断结果没问题,再扩大到全表。

2.3 readmatrix 与 readcell:两个“口味单一”的快读函数

readmatrix专门为纯数值表格设计,返回值是一个普通矩阵,而不是table:

M = readmatrix('clean_data.xlsx', 'Sheet', 'Sheet2', 'Range', 'B2:E100');

只要目标区域每个单元格都是数值,readmatrix的读取效率很高,内存占用也比table小很多。可一旦区域内出现一个文本单元格,整列都会变成NaN,且没有提示。因为矩阵本身只能存一种类型,文本无法保留,只能丢弃。所以readmatrix只适合“绝对干净”的数值表。

readcell则完全不挑类型,所有数据都放进元胞数组,包括文本、数值、日期格式等:

C = readcell('mixed.xlsx', 'Sheet', 'Sheet1', 'Range', 'A1:F50');

readcell的好处是原样保存单元格,适合拿来做数据体检,比如逐格检查某个意外数值是怎么混进去的。缺点也很直接:元胞数组处理起来不如table方便,对单列数据取均值得先手动转成数值向量。这三个函数本质上是针对不同“形状”的数据写的:readmatrix数值矩阵、readcell元胞数组、readtable表格。先把Excel表按“纯数值、混合表格、文本为主”分类,选型就不会出错。

2.4 选错函数时的典型特征

如果你不确定自己选错了函数,可以对照下面的现象自查:读出来所有数值列都变成NaN,多半是用了readmatrix而表里存在文本列;读取后数据全部挤在第一个sheet里无法区分工作表,是忘了传Sheet参数;读出来的列名变成了Var1、Var2,是ReadVariableNames默认行为与文件表头不一致;数据总是差一行,是因为readtable默认把首行当变量名,而实际首行是标题或备注。

这些特征本质上都源于读取函数对“表头存在性”和“类型统一性”的假设。尤其是readtable,它默认Excel文件第一行是变量名。如果你的Excel前两行是一句中文标题,再往下才是字段名,就必须用ReadVariableNames=false,再手动设置变量名。没有统一公式,拿到文件后先readcell看一小块,比猜参数效率高得多。

3. 读取范围、多工作表和指定列:把readtable用到顺手

3.1 Sheet与Range参数:从整表读取改成定点读取

readtable默认读取指定Excel文件的第一个工作表整表,但实际工作中一个工作表往往有几十列、上万行,而真正关心的只有一角数据。每次全表读取既慢又容易引入无关区域的脏数据,所以我一般会先把Range收窄。

T = readtable('project_data.xlsx', 'Sheet', 'Sheet3', 'Range', 'A1:D500');

Sheet参数可以传工作表名,也可以传索引。为了可读性,我推荐传名称;不过需要注意,如果工作表名称是中文,在字符串里直接写没问题,但不要混用单双引号。Range参数支持'A1:B10'这种完整矩形区域,也支持'A1'单格,但很少用。如果你不确定区域边界,可以用readcell快速探测首列最后一个非空行,再回头设置Range。

Range的一个隐藏好处是能避开表头上面的公司注释和Logo图片。Excel文件里常见的“首行大标题”,在readtable眼里会被当成变量名,导致后续所有列名错位。用Range='A2:F200'就能直接跳过标题行,这种情况下最好配合ReadVariableNames=true,表示第二行才是真正的变量名。

3.2 批量读取多个Sheet:用sheetnames做循环

一个Excel文件里有多个结构相同的工作表时,手动写多次readtable很蠢,而且一旦增加Sheet就要改代码。新版本MATLAB提供了sheetnames函数,可以直接拿到全部工作表名:

sheets = sheetnames('project_data.xlsx'); allData = cell(length(sheets), 1); for i = 1:length(sheets) allData{i} = readtable('project_data.xlsx', 'Sheet', sheets(i)); end

这里的sheets(i)是字符串数组元素,readtable的Sheet参数可以接收字符串数组索引值。循环结束后allData是元胞数组,每个元素是对应工作表的table。如果你希望把所有表按行拼接,可以用vertcat:

merged = vertcat(allData{:});

vertcat要求每个table的变量个数和变量名完全一致。如果不同Sheet的列顺序有一点点差异,vertcat就会报错,因此我习惯先检查width和variableNames。另外,sheetnames返回的是工作表显示名称,不是内部Sheet1这种代号,所以不会出现错位。

3.3 指定列、跳过列与自定义类型:用detectImportOptions接管推断

自动推断在大多数表格上够用,但遇到“某列是文本数字,另一列是日期,还有两列是重复编号”时,手动调整类型更可控。detectImportOptions可以读取文件的工作表内容和推断规则,然后你再修改规则,最后传给readtable:

opts = detectImportOptions('sales.xlsx', 'Sheet', 'Sheet1'); opts.SelectedVariableNames = {'月份', '销售额', '备注'}; opts.VariableTypes = {'datetime', 'double', 'string'}; T = readtable('sales.xlsx', opts);

detectImportOptions返回一个ImportOptions对象,它包含了每一列的名称、类型、缺失值规则等。上面代码中SelectedVariableNames指定只读取三列,VariableTypes指定这三列的目标类型。注意VariableTypes的顺序要和SelectedVariableNames一一对应,否则会张冠李戴。

如果只想修改某一列,用setvartype更加直接:

opts = detectImportOptions('sales.xlsx'); opts = setvartype(opts, {'销售额'}, 'double');

setvartype的好处是只改指定列,其他列继续走自动推断。这个方法在Excel里“销售额”一列是文本格式时特别有用,因为你明明想当数值处理,却总被推断成cellstr。指定为double后,如果个别单元格确实无法转换,读取阶段就会报错,而不是默默变成NaN。

3.4 多级表头和空白行怎么办

多级表头在财报中很常见,第一行是“一季度”,第二行才是“销售额”,两层表头叠在一起。直接用readtable会把第一行当变量名,第二行变第一行数据,整张表乱掉。我一般分两步:先用readcell看前几行结构,再决定Range起点。

假设真正的表头在第2行,数据从第3行开始,可以这样读:

T = readtable('finance.xlsx', 'Sheet', 'Sheet1', ... 'Range', 'A3:H400', 'ReadVariableNames', true);

然后手动改名:

T.Properties.VariableNames = {'月份', '销售额', '成本'};

这种方法很笨,但最可靠。ReadVariableNames参数默认是true,它与Range的配合规则是:读取时把Range首行当作变量名。如果Range里有空行,readtable会保留为NaN或missing,导致数据行数膨胀,我通常会在设置Range前用readcell确认没有全空行,尤其是最后几行容易混进一堆无效数据。

4. 数据类型与格式陷阱:日期、文本型数字、缺失值的规范化方案

4.1 日期读进来是序列号:先确认是Excel序号还是字符串

Excel内部存储日期时通常是一个整数序列号,比如2024年1月1日实际上是45292。当用readmatrix直接读取日期列,得到的自然是一堆四万多五万多的数字,看起来像乱码,其实没读错,只是没转换。要转回MATLAB日期,可以使用:

dateValues = readmatrix('dates.xlsx', 'Sheet', 'Sheet1', 'Range', 'B2:B100'); realDates = datetime(dateValues, 'ConvertFrom', 'excel');

MATLAB的datetime函数支持ConvertFrom参数,设为excel时会把Excel序列号转换为datetime数组。注意Excel的日期起点与MATLAB不完全一致,但ConvertFrom已经处理了偏移,所以不需要手动加减天数。

如果用readtable读取,情况会不同。新版MATLAB在部分场景下会自动把Excel日期识别成datetime,但前提是detectImportOptions判定该列为日期类型。如果推断成double,就需要用上面的方式转换。我建议先执行head(T)看一眼输出,如果日期列是double而不是datetime,马上手动转,不要等到画图时才发现横轴全是数字。

4.2 文本型数字:看起来是数字,一求平均就报错

Excel单元格左上角有绿色小三角,往往是“文本型数字”。这列数据肉眼看到的是123、456,但底层是文本。readtable自动推断时有可能把整列推断成cellstr,于是你用mean(T.销售额)会直接报错,提示未定义操作符。

解决思路是把文本转数值:

T.销售额 = str2double(T.销售额);

str2double接收元胞数组时能按元素转换,无法转换的文本变成NaN。这里要注意:如果T.销售额是字符串数组而不是元胞数组,str2double也能处理,但空字符串会变成0,而不是NaN。若要区分“原本是缺数据”和“数字0”,建议先检查有没有空串。还可以在读取阶段用setvartype强制指定为double,让MATLAB在导入时完成转换:

opts = detectImportOptions('pay.xlsx'); opts = setvartype(opts, '实付金额', 'double'); T = readtable('pay.xlsx', opts);

如果某单元格是“待确认”这类非数字文本,这种强制转换会让读取直接报错,至少能让你一眼看到脏数据在哪一行。另一种方案是先读成文本,再定位不能转换的位置。这里没有万能解,但我通常先强制转换,真报错了再回到元胞数组看数据。

4.3 空单元格与缺失值:NaN、missing与空字符串的差别

Excel空单元格在不同函数里的表示不一样。readmatrix把空单元格当0还是NaN,取决于文件格式和是否显式写0;更常见的是数值列空单元格变成NaN。readtable中数值列空单元格为NaN,文本列空单元格为missing,字符串列则可能变成<missing>。三者的判断方式不同:

T = table([1; NaN], ["文本"; missing]); ismissing(T.Var2) isnan(T.Var1)

ismissing是通用判断,能同时识别NaN、missing和空字符串,这是最省事的。我强烈建议不要在Excel里用空格表示缺失值,因为空格会被读成空字符串而不是missing;后续用ismissing能识别空字符串,但用isnan就会漏掉。如果你发现一列文本中有大量空字符串,可以先统一替换为missing:

T.备注(T.备注 == '') = missing;

替换后再调用ismissing就干净了。另外,不要依赖fillmissing处理缺失值之前先检查是否类型匹配,否则会把数值列和文本列一起拉下水。

4.4 单元格内换行和特殊字符:小心行数莫名变多

Excel单元格支持Alt+Enter换行,有时你以为是一个单元格,但在readtable读进来后可能被拆成多行,尤其是使用文本文件格式导入时。另一个坑是金额数字里带千分位逗号,比如1,234,readtable会把它当文本而不会自动去掉逗号。

处理方法是尽量在Excel源头清理,或者在读取后用正则表达式删除干扰字符:

T.金额 = str2double(strrep(string(T.原始金额), ',', ''));

strrep是字符串替换,把逗号去掉后再转数值。这个方案适用于英文千分位;如果用户习惯用中文全角逗号,也要先替换成全角转半角。单元格内换行比较麻烦,因为它会破坏行对应关系,我一般用readcell定位真正有换行的列,再决定是保留还是清洗。如果换行符只是文本列的备注,不影响数值列,通常保留即可;如果地址列里有换行,在导出成csv时会导致行列错位,这时建议把换行符替换为空格:

T.地址 = strrep(string(T.地址), newline, ' ');

5. 常见问题与避坑:读取报错、乱码、大文件卡死的排查路径

5.1 报错“Unable to read file”:八成不是数据问题

现象:readtable('D:\data\销售.xlsx')报错Unable to read file,或提示找不到文件、无权访问。
原因:最常见的是Excel文件正被另一个进程打开占用,或者路径中的反斜杠和中文目录在部分环境不被识别。
解决:先按照下面顺序排查:

filePath = fullfile('D:', 'data', '销售.xlsx'); disp(exist(filePath, 'file')) [~, sheets] = xlsfinfo(filePath);

exist返回2表示文件存在,返回0说明路径或文件名错了。xlsfinfo能读到工作表信息说明文件可用。如果文件确实被Excel开着,关闭Excel再跑一次。另一个隐蔽原因是文件名后缀名显示为.xlsx,但实际是Excel 97-2003格式.xls,这时需要把后缀统一改成.xls再读取。不要相信文件的显示扩展名,用dir看一眼真实文件名最有把握。

5.2 数值列变成全NaN:先怀疑“脏数据区”

现象:表格看起来全是数字,用readmatrix读取后M里整列NaN,或readtable读出来列类型是cellstr。
原因:某个单元格存在肉眼没注意的文本,比如单位“元”、空格、下划线、错乱字符。只要区域内混进一个不是数字的单元格,readmatrix的整列结果就会被影响;readtable则可能改变整列类型。
解决:先用readcell把原始区域完整读出来,定位异常值:

C = readcell('data.xlsx', 'Sheet', 'Sheet1', 'Range', 'B2:B500'); nonNumeric = cellfun(@(x) ~isnumeric(x), C); badRows = find(nonNumeric); disp(C(badRows))

cellfun逐个判断单元格是否为数值,返回逻辑数组,find找出异常行。看到具体内容后,选择在Excel中清理或手动替换。这里最难查的是“看起来是空格”的字符,比如全角空格、&nbsp;,用strtrim清理后再转数值。

5.3 大文件读到头秃:先看Excel文件本身,再考虑分割读取

现象:20MB的xlsx文件,readtable跑了五分钟还没结束,MATLAB窗口一直显示“正忙”。
原因:xlsx本质是zip压缩包,解析过程需要解压全部XML,Excel本身的公式缓存、格式定义也会让读取变慢。尤其是一个Sheet里有十几万行、几十列,纯手工逐单元格解析很吃力。
解决思路有三个步骤。第一步,确认是否有必要读全表,如果业务只需要几列,用Range和SelectedVariableNames缩小范围。第二步,把Excel另存为CSV格式再读取,CSV纯文本读取速度远快于xlsx:

T = readtable('large_data.csv');

第三步,如果CSV也有几百MB,可以考虑按块读取。readtable本身不支持跳过任意行数,但可以先读成datastore:

ds = datastore('large_data.csv', 'TreatAsMissing', 'NA'); T = read(ds, 20000);

datastore是处理大文本表的常用工具,它每次读取指定行数,适合分批处理。注意它不像readtable那样支持Range,但能通过SelectedVariableNames指定列。在处理大文件前,最好先用whos估算内存占用,避免读进来直接内存溢出。

5.4 中文列名和重名列名:readtable给的神坑

现象:Excel首行有两个“金额”列,readtable读完后变量名变成了金额和金额_1,后续用T.金额引用却拿到第一列,而第二列怎么都引不对。
原因:MATLAB运行变量名不能重复,自动重命名是保护机制,但重命名规则并不直观。
解决:读取后主动查看并重命名:

T.Properties.VariableNames T.Properties.VariableNames = {'金额A', '金额B'};

更保险的方式是在readtable前先手动指定变量名范围,或读取后立即重命名。如果中文列名带有空格、减号或不规则字符,T.列名这种点号语法会出错,只能改用T.('列名')引用:

T.('销售金额(元)')

这种情况最容易发生在从第三方系统导出的Excel中,变量名千奇百怪。我一般把所有列名统一改为英文字段名,避免后续脚本里到处写中文变量名,既难排查又容易踩编码坑。

5.5 COM/ActiveX方案:能用但不要优先

现象:使用actxserver('Excel.Application')方式读取Excel,感觉能完全控制Excel,但脚本运行不稳定,有时候能读出数据,有时候进程卡死。
原因:通过COM接口启动了一个真实的Excel进程,一旦代码异常终止,这个进程会残留并占用文件。
解决:优先使用原生readtable和readmatrix;只有遇到极其特殊的Excel格式、需要触发Excel公式计算后才能读取时,才考虑COM方案,并且要在finally中关闭进程:

e = actxserver('Excel.Application'); try e.Workbooks.Open(filePath); % 触发计算并读取 catch error('COM读取失败'); finally e.Quit; delete(e); end

这个方案在日常数据整理中几乎没必要,碰到密码保护或复杂宏表格时,与其在MATLAB里对抗Excel,不如先手动另存为干净的xlsx。

6. 进阶用法:把读取Excel封装成可复用的函数

6.1 封装函数并处理多Sheet

读Excel最大的重复劳动是“找Sheet、找Range、设置类型”这三件事。我习惯封装成一个简单函数,输入文件路径和Sheet名,输出table,并且自动处理文件不存在的情况。

function T = readExcelTable(filePath, sheetName) arguments filePath (1,1) string sheetName (1,1) string end if ~exist(filePath, 'file') error('文件不存在: %s', filePath); end T = readtable(filePath, 'Sheet', sheetName); T.Properties.VariableNames = matlab.lang.makeValidName(T.Properties.VariableNames); end

arguments是MATLAB R2019b以后支持的参数校验语法,这里限制输入必须是单个字符串。函数先用exist检查文件,再用readtable读取,最后用matlab.lang.makeValidName把所有列名转为合法MATLAB变量名,避免中文和空格带来的后续问题。调用时只需要一行:

T = readExcelTable('sales.xlsx', 'Sheet1');

6.2 验证封装函数,并控制Range

封装只是第一步,真正让读取稳定的关键是验证。我会强制在测试时打印前几行和变量类型,确认没有意外:

assert(istable(T), '返回值不是table'); head(T) summary(T)

summary能直观看到每列的类型和缺失值数量,这一步能发现很多自动推断问题时在传输过程中被掩盖的细节。从那以后我每次拿到新Excel文件,都强制走一遍“看形状、选函数、验类型、查缺失”的流程,不再因为急着读取数据而跳过体检。希望这篇文章里的方法和避坑记录,也能帮你把MATLAB读取Excel这件事从“总是出问题”变成“一次读对”。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询