0 前期教程
1 前言
在前期教程中,我总结了MATLAB的基本使用及其在各专业领域中的基本应用,给人一种感觉就是MATLAB似乎只适合干一些高大上的活,但实际上,MATLAB的功能非常强大(“除了生孩子其他都会”),因为它本身就是一个能够运行一种包含丰富“库函数”的编程语言(一般称之为MATLAB语言)的编译器,就像是PyCharm能够运行python一样,所以对于使用者来说,为了能够真正发挥MATLAB的功能,除了掌握那些专业领域的相关函数外,还需要对编程语言和编译器本身使用方法有一个完整的认识。
下面记录的是本人在实践过程中一些容易“卡壳”的问题,主要涉及MATLAB语言的一些基本用法和MATLAB软件相关操作。帮助打通实践的基本关节。
本博客所有代码基于MATLAB 2019b,后续会随着实践不断更新内容。
2 输入输出
在很多使用MATLAB的场景中,输入参数变量、读文件、写文件都是第一步需要完成的,可见输入输出函数的重要性,这也是提升MATLAB使用效率的一个重要方面。
2.1 input
x = input(prompt)
str = input(prompt,'s')
这个函数可以实现带有提示的输入,其中,里面的参数prompt
即为提示字符串。如果加上后面的参数's'
,则返回输入的文本,即返回值为字符串,非数字量。
2.2 load
load
函数一般用来加载MTALAB变量,即.mat
格式的文件。其语法格式为
load filename
有时候为了方便代码交流,会将MATLAB中处理好的数据分享出去,方法就是直接在命令行中输入save
,或再加上文件名,这样就会将工作区的变量打包成一个.mat
文件,如果不加文件名,默认为matlab.mat
。或者直接在工作区右键,选择“保存”,如下图所示。
2.3 importdata
importdata函数其实与MATLAB主页的导入数据是一致的,
只不过一个是代码一个是手动操作。如果只有单个文件,那肯定是手动操作要更加便捷,但是如果有多个文件,用代码就更加优雅。
importdata
函数主要适用于比较规整的文本文件,这个所谓规整就是排列得要是矩阵的形式,同时数据只包含数值,不含字符串,如果既包含字符串又包含数值,那么读取出来的就只能是整体字符串形式了。其语法如下所示。
A = importdata(filename) %从文件中读取
A = importdata('-pastespecial') %从剪切板中读取
A = importdata(___,delimiterIn) %设置分隔符
A = importdata(___,delimiterIn,headerlinesIn) %headerlinesIn设置从第几行开始读取数据
此外,importdata
函数还能读取图片数据,其返回值数据类型为uint8
。
A = importdata("test.jpg")
image(A) %显示图片
2.4 disp
disp
函数用来显示变量、数据和字符串等,算是一个“万金油”,但是它显示的字符串是不转义的,也就是\n
不能识别为回车。且它显示变量不会显示变量名。
2.5 fopen & fclose
在学习下面的文本文件读写之前,首先要理解MATLAB中文件的打开和关闭。
不管是读取还是写入一个文件,首先要调用fopen
打开文件,获取文件句柄fileID
,之后再对fileID
进行操作(文件名差不多就没用了)。其语法如下所示。
%% fopen
fileID = fopen(filename);
fileID = fopen(filename, permission);
其中,permission
参数即为文件打开的权限,其常用的参数如下所示。
文件打开后,并完成了读写操作,最后一定要记得调用fclose
函数关闭打开的文件,其基本语法为
fclose(fileID) %即直接操作文件句柄即可
如果不及时关闭打开的文件,且积累的数量较多时,MATLAB运行可能会报错:
2.6 fscanf & fprintf
fscanf & fprintf这一套函数主要用于文本内容比较复杂的情况,如字符串和数据杂糅时。
其中fscanf
这个函数主要用来读取文本文件中的固定格式的数据,和C语言中类似。其基本语法如下所示。
A = fscanf(fileID,formatSpec)
A = fscanf(fileID,formatSpec,sizeA)
[A,count] = fscanf(___)
其中,fileID
就是上面调用fopen
函数的返回值,formatSpec
参数为数据格式,如整数使用%d
,小数使用%f
等,与C语言中scanf函数类似。常用的如下图所示。
最后是sizeA
参数,它是描述输出数组A维度的参数,其取值如下所示。
fprintf
函数相比于fscanf
函数只能读取文件外,它除了写文件,还可以输出到命令行。其语法如下所示。
fprintf(fileID,formatSpec,A1,...,An) %输出到文件
fprintf(formatSpec,A1,...,An) %输出到命令行,以固定格式输出
这里的formatSpec
参数与上面有所不同,它对应的是C语言中printf函数,可以设置打印的对齐方式、字段宽度、精度等信息,详细内容建议参考帮助文档。
2.7 textread & textscan
textread
和textscan
主要用于读取复杂的文本文件,尤其是存在字符串和数据混杂时,建议首选这两个函数。从帮助文档可以看出,官方推荐使用的是textscan
函数,功能更加强大,且textread
函数的帮助文档已经不再更新了,所以这里主要介绍textscan
函数。其语法如下所示。
C = textscan(fileID,formatSpec)
C = textscan(fileID,formatSpec,N)
C = textscan(chr,formatSpec)
C = textscan(chr,formatSpec,N)
C = textscan(___,Name,Value)
其中,formatSpec
参数最为重要,它决定了以什么样的格式去读取字符串,内容较多,建议直接参考help文档。参数N
表示对该格式应用的次数(一般是在一行内),参数chr
为字符串向量,相当于它支持的数据来源除函数外还有字符串。最后的name-value参数常用的有分隔符'delimiter'
和注释符'commentstyle'
.
2.8 sprintf & sscanf
sprintf
和sscanf
这套函数功能与C语言中对应的同名函数作用差不多,sprintf
函数的功能就是格式化输出字符串,可以实现将数据转换为字符串格式,看似它的功能和fprintf
差不多,但是重要区别在于后者是输出到命令行和文件,而前者是输出到一个定义好的字符串,可以用于后续使用。
相对应的,sscanf
的功能和fscanf
差不多,但是区别也是同样的,后者的输入只能是文件,而前者的输入可以是字符串,这样就能实现从字符串中提取数据,然后给后续代码使用的功能。
这两者的语法如下所示。
%% sprintf
str = sprintf(formatSpec,A1,...,An) %A1~An是变量
[str,errmsg] = sprintf(formatSpec,A1,...,An) %errmsg是错误信息,没有则为空
%% sscanf
A = sscanf(str,formatSpec) %str为指定要读取的字符串
A = sscanf(str,formatSpec,sizeA) %sizeA约定返回数组A的维度
再看几个简单的例子
sprintf:
formatSpec = "The current time is: %d:%d %s";
A1 = 11;
A2 = 20;
A3 = 'a.m.';
str = sprintf(formatSpec,A1,A2,A3)
% 输出结果
str =
"The current time is: 11:20 a.m."
sscanf:
>> str = "2.7183 3.1416 0.0073"
str =
"2.7183 3.1416 0.0073"
>> A = sscanf(str,'%f',[1 3])
A = 1×3
2.7183 3.1416 0.0073
2.9 xlsread & xlswrite
这套函数主要用于excel文件的读写,虽然在帮助文档中不推荐使用这两个函数,但是仍然可以使用,且个人认为也挺好用的。
其语法格式如下所示。
%% xlsread
num = xlsread(filename)
num = xlsread(filename,sheet) %指定sheet
num = xlsread(filename,xlRange) %xlRange指定读取范围,采用excel中设置范围的语法,如A1:C3
num = xlsread(filename,sheet,xlRange)
num = xlsread(filename,sheet,xlRange,'basic')
[num,txt,raw] = xlsread(___) %返回分类好的数据和文本
%% xlswrite
xlswrite(filename,A)
xlswrite(filename,A,sheet)
xlswrite(filename,A,xlRange)
xlswrite(filename,A,sheet,xlRange)
这两个函数使用比较简单,关键是要注意数据类型,有时候会得到table
格式的文件,如果需要将其转换为一般的数组或矩阵,可以使用table2array
指令实现数据格式的转换。【xx2xx
这类的指令很常见,如cell2table
,如果需要转换数据,但不知道可以转换为哪种类型,可以先输入cell2
,然后按下Tab键进行代码提示】
2.10 csvread & dlmread
csvread
主要用于读取.csv
格式的文件,其特点是以逗号作为分隔符;dlmread
函数主要用于读取ASCII分隔的数值数据文件,即不含有字符串。
目前官方给出的help文档已经不再推荐使用这两个函数了,而是使用readmatrix
和writematrix
,其中readmatrix
文件支持读取的文件格式有
- .txt、.dat 或 .csv(适用于带分隔符的文本文件)
- .xls、.xlsb、.xlsm、.xlsx、.xltm、.xltx 或 .ods(适用于电子表格文件)
3 小数位数的保留
参考链接: MATLAB中小数位数的设置方法 - CSDN
3.1 取整函数
3.1.1 fix—向零取整
向零取整,即Round towards zero, 也叫截尾取整,其语法如下所示。
>>fix(3.6)
ans = 3
3.1.2 floor—向负无穷取整
向负无穷取整,Round towards minus infinity,即求不超过x的最大整数,也叫高斯取整,其语法如下所示。
>> floor(-3.6)
ans = -4
3.1.3 ceil—向正无穷取整
向正无穷取整,Round towards plus infinity,即求大于x 的最小整数,其语法如下所示。
>> ceil(-3.6)
ans = -3
3.1.4 round—向最近整数取整,四舍五入
向最近整数取整,Round towards nearest integer,即四舍五入取整,其语法如下所示。
>> round(3.5)
ans = 4
3.2 保留小数位数
3.2.1 vpa
该函数对常量适用,对变量不适用。保留的是有效数字(significant digits)位数,取四舍五入值。
>>a=3.251
a=3.251
>>vpa(a,2)
3.3
如果不加后面的参数2,则默认保留32位有效数字。
另外,这个函数还可以用在多项式系数化简。如果有如下多项式
执行指令
vpa(p, 5);
则可以将该多项式系数全部只保留5位有效数字。
3.2.2 sprintf
该函数可对变量使用,但使用后,出来的数据类型为字符类型,如要进行后续运算,还需要将字符转换为数值格式。
>>sprintf('%.2f',a)
3.25
3.2.3 roundn
该函数可对变量使用,操作后,变量仍为数字类型。
>>roundn(a,-2)
3.25
注意和rand,randn之间的区别!!!!!
4 类与函数
4.1 类的定义与使用
补充一点:在MATLAB中调用自定义函数时,如果有输出参数,且调用时不加分号时,会默认输出一个参数,就会显得输出“莫名其妙”
网上有很多关于MATLAB类的相关教程,但写得都不是很完整,没有从定义到使用按顺序讲解,找了很多资料,最后终于找到这篇参考链接。对于有C++类的基础知识的人来说,这篇博客可以概述为几点:
- 构造函数可以不需要
- 类中的成员函数都要在所有参数的前面多加一个参数,即第一个参数必须是
class_obj
,当然也可以命名为其他变量,这个参数的作用是 调用类内变量,使用方法就是class_obj.A = xxx
(A为类内成员变量),如果不需要调用类内成员变量,可以用~
代替。 - 类的继承符号为
<
,如果继承多个类,使用&
连接 - 使用类时,格式为
A = Student
,即定义一个Student
类的实例A
- 类内成员函数可以有多种类型,方法就是在
methods
后面加上括号,括号内为修饰词 - MATLAB也可以像C++一样,在一个文件中定义类,但只声明成员函数,成员函数的定义在另一个文件中。
4.2 带缺省参数的函数【参数有默认值】
最近学习C++时,对它提供的带缺省参数的函数挺感兴趣,突然想到常用的MATLAB是怎么实现缺省参数的?便查了一通资料,记录一下。
常见的高级语言中,都支持在定义函数时,用=给形参赋一个默认值,这样如果不传这个参数那它取的就是默认值。但是MATLAB不支持这种函数定义方法,而是采用其他方式来实现。
一般MATLAB想要实现缺省参数,有两种方式。一种是使用 inputParser 来调用函数输入解析器,具体可以使用help查看。这种方式的好处在于不用管缺省参数的位置和顺序,只要键值对对应即可,但使用较为麻烦。
还有一种方式是判断参数的个数,使用 nargin,nargout,varargin,varargout 等变量来实现。具体可以参考这个链接。
此外,这篇博客中提到了还可以使用exist来判断参数,可以学习一下。
4.3 function handler(函数句柄)是什么?
最近用MATLAB解遗传算法的问题,发现一个问题:那就是有一个参数不管怎么传都不对(估计这也是很多人对MATLAB指令使用的烦恼点之一吧),报错信息是说要填function handler,不知道这个东西应该怎么来得到,找了一些资料,找到一个教程链接,总结如下:
- 函数句柄可以引用已有函数。如zeros,则可以使用
f=@zeros
来实现“调用”; - 函数句柄可以是自己建立的函数。 这种类型一般是要先自己建立一个函数文件
function a = myfunc (input_arg)
,然后调用的时候是f = @myfunc
; - 单独建立一个句柄。 有时候函数类型并没有那么复杂,一个表达式即可解决,而且也不需要复用,那么就可以单独列一个表达式,并引用它的句柄。方法如下:如果要创建
f(x) = x^2+sin(x)
这个函数,则可以使用g = @(x) x^2+sin(x)
,即@
符号后面加上一个括号,然后里面填表达式的自变量。那如果是多变量的函数呢?比如f(x, y) = x^2+y^3
,需要记住,这里不能将x,y写到自变量中,而是要将这两个自变量变成一个向量,然后再传,故:g = @(x) x(1)^2+x(2)^3
。
4.4 函数中的变量不会输出或出现在工作区?//2022.9.3
这个如果联想C语言就很容易理解了,因为函数里面都是临时开辟的内存区域,使用完了之后就直接释放掉,不会保留函数执行过程中产生的变量。
但是很多时候又需要看到中间某些变量的输出结果,这个时候要么选择不加分号,让数据直接输出,要么就是将需要观察的变量设置为输出变量,且调用的时候不加输出,这样就能在命令行中看到需要观察的变量了。
5 软件相关设置与操作
5.1 如何设置默认工作路径
之前多次打开matlab都发现一个问题,就是工作路径每次都定位到其根目录下的bin文件夹,不胜其扰,而且发现在预设里面设置路径也不管用。如下图所示。
最后找到一个更加简单的方式:【参考链接】
首先找到MATLAB的快捷方式,右键打开属性。将快捷方式中的起始位置改为你的工作路径即可。非常简单,而且亲测有效!
5.2 安装附加功能
在安装MATLAB时,即使全选了给出的工具箱,但是仍然有可能会用到未安装的功能,【至今没搞懂附加功能(Add_On)和工具箱(ToolBox)之间的区别~】所以MATLAB提供了一个安装附加功能的选项,但是很遗憾的,关于这部分内容,网上的说法似乎很不统一,也没有一个确定有效的方法,经过我自己的摸索,发现官方给的方法还是最为有效且方便的。
安装附加功能时,首先在主页点开“获取附加功能”,如下图所示。
不过需要注意的是,进入这个界面需要科学上网,否则不能检索。
进入到界面后,在搜索框中搜索你想要安装的附加功能。然后点开,可以在名称下面看到安装的按钮。注意:不要直接点击安装,要点击下载,经过测试,即使科学上网也不能直接安装!。
点击下载后,记住下载的路径,基本是一路next,不不需要更多的设置。下载完毕后,将下载的内容复制到MATLAB的安装路径中,并在MATLAB中进入到这个路径。如下图所示。
进入到这个路径之后,找到一个执行程序Install_supportsoftware.exe,并双击执行,然后它就会自动检测该路径下的安装包并自动安装,剩下的就是等待了。
6 其他【不断更新!】
6.1 判断一个矩阵是否是零矩阵
6.2 文件命名规则 //2022.10.3
-
文件名不能与matlab的内部函数名相同:m文件名的命名尽量不要是简单的英文单词,最好是由大小写英文/数字/下划线等组成。原因是简单的单词命名容易与matlab内部函数名同名,结果会出现一些莫名其妙的错误;【这个MATLAB中会有提示】
-
首字符不能是数字或下划线:如果首字母是数字或者下划线,MATLAB通常提示找不到该文件;
-
文件名及路径只能有英文字符, 不可以包含中文;
-
m文件起名不能有空格:尽量用下划线代替空格
综上,m文件名的命名最好是由大小写英文字母/数字/下划线等组成。
6.3 MATLAB实现函数参数实时提示
众所周知,MATLAB输入代码按下Tab键可以有提示,但是不能像VS Code那样有实时提示,因此在网上找到一个教程,“大致” 实现了这个功能。
其原理就是将函数提示的快捷键改为左括号,即Shift + 9
,如下图所示。
6.4 MATLAB进行表达式求值
之前在使用MATLAB一直有一个困扰,那就是MATLAB中的含有变量的表达式和矩阵怎么比较方便地代入数值计算?之前对于这种问题,我一般的做法就是给变量赋值之后重新执行一遍表达式,这样显然不太方便使用。这里介绍两种方式。
-
eval()
其用法如下图
这个eval函数就是用来执行表达式的,其help文档的主要部分如下图所示。
用eval函数有一个问题,那就是定义的变量因为赋值之后就没了,也就是如果要使用其符号变量,需要重新定义,略微有点麻烦。 -
subs
这个函数也是第一次看到,通过速览其help文档,可以知道它实际上就是将旧的表达式赋予新值,然后产生一个复制品。
使用方式如下图所示。
需要注意的是,返回的内容似乎和原内容的类型会保持一致,所以可以看到一个诡异的现象:虽然ans是一个常值矩阵,但是工作区上面显示它的类型是sym,即仍然是符号变量,但经过测试发现进行一些基本的运算是没问题的。使用方面有待后续继续探索。
6.5 代码格式化 //2022.12.04
6.6 获取附加功能 //2022.12.20
一般来说,正常激活的MATLAB,是可以通过点击主页的“添加附加功能”按钮来实现添加的,最多是挂个梯子“附魔”,但是今天突然发现好像不好使了,体现为点击这个按钮会弹出一个更新license的弹窗,如下图所示。
我一开始以为是MATLAB的激活出现问题了,但是在网上也没有找到能够更新许可证的办法,于是我想到之前安装MBeautifier的步骤,完全可以找到对应的下载网址,然后再单独下载,最后再通过添加路径的方式安装。
问题就在于这个网址是什么,发现有一些博客对这个网址藏着掖着,不知道是觉得太好找了还是其他什么原因,我最后终于找到这个链接:https://www.mathworks.com/matlabcentral/fileexchange/,剩下的就是和上面差不多,添加路径即可。
6.7 添加进度条
当运算任务比较大时,MATLAB可能会卡住,为了了解当前是正常在运算还是软件卡住了,一般会在命令行中输出一些进度标志,来了解当前的计算进度,但是这样会占用命令行的范围,所以建议采用进度条来指示当前计算进度。
这里用到的函数是waitbar
,其语法如下所示。
%% 建立进度条
f = waitbar(x,msg)
f = waitbar(x,msg,Name,Value)
%% 更新进度条
waitbar(x)
waitbar(x,f)
waitbar(x,f,msg)
其中,参数x
表示进度大小,用一个[0, 1]之间的小数来表示。参数msg
表示对话框上显示的信息,这个函数会建立一个非模态对话框,即在出现进度条后MATLAB的其他窗口还能继续访问。此外,其Name, Value参数对中有一个是'Name'
,用于指定弹出的对话框的名字。
bar = waitbar(0,'读取数据中...'); % waitbar显示进度条
A = randn(1000,1); % 随机生成1000行1列数据
len = length(A); % 读取A矩阵长度
for i = 1:len % 循环1000次
B(i) = i^2; % 求平方,无意义,示例函数
str=['计算中...',num2str(100*i/len),'%']; % 百分比形式显示处理进程,不需要删掉这行代码就行
waitbar(i/len,bar,str,'Name','Result'); % 更新进度条bar,配合bar使用
end
close(bar) % 循环结束可以关闭进度条
6.8 显示程序运行时间
在一些情况下,会需要知道某段程序所用的时间,一般是使用tic
和toc
这一套函数来实现。其中tic
是启动定时器,然后开始计时,相当于设置一个起点;而toc
在执行该命令时,会返回 当前时刻距离最近一次tic
的时间,单位是秒。
t = zeros(1,100);
for n = 1:100
A = rand(n,n);
b = rand(n,1);
tic; %随着循环的进行,会不断刷新起点
x = A\b;
t(n) = toc; %取最近一次起点到当前时刻所用时间
end
plot(t)