MATLAB中文论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

MATLAB中文论坛 门户 技术专栏 MATLAB基础 查看内容

MATLAB中文论坛常见问题归纳

2016-9-12 14:48| 发布者: ilovematlab| 查看: 19240| 评论: 13|原作者: wuyou136

摘要: 这里总结归纳了一些论坛中经常重复常见的一些MATLAB问题。一方面可以避免作重复性回答,另一方面为 MATLAB 初学者提供一些常见问题的解决办法,希望对大家有所帮助。 ... ... ... ...

目录:

论坛经常会碰到一些重复出现的问题,这里总结归纳了一些比较常见的基础问题。一方面可以避免作重复性回答,另一方面为 MATLAB 初学者提供一些常见问题的解决办法,希望对大家有所帮助。感谢@winner245@kaaaf123@ljelly 对内容提供的一些建议,如果你觉得还有什么值得归纳的问题,可以在文章底部评论中提出,我们共同探讨。

求解方程

求解方程通常有两种方法,符号求解和数值求解。

1. solve(Symbolic Math Toolbox)

通常在不确定方程是否有符号解的时候,推荐先使用 solve 进行尝试,因为 solve 相比于数值求解来说,它不需要提供初值,并且一般情况下能够得到方程的所有解。对于一些简单的超越方程,solve 还能够自动调用数值计算系统给出一个数值解。

solve 的常见调用形式:
sol = solve(eq)
sol = solve(eq,var)
sol = solve(eq1,eq2,…,eqn)
sol = solve(eq1,eq2,…,eqn,var1,var2,…,varn)
eq 为符号表达式,var 为指定的要求解的变量。如果不声明要求解的变量(第一和第三种形式),则 MATLAB 自动按默认变量进行求解,默认变量可以由 symvar(eq) 确定。
%例:求解方程组:  x+y = 1,  x-11y = 5

syms x y      %声明符号变量
eq1 = x+y-1;
eq2 = x-11*y-5;
sol = solve(eq1,eq2,x,y);
x = sol.x
y = sol.y
solve 求得的解通过结构体的形式赋值给 sol,然后再通过 x = sol.x 和 y = sol.y 分别赋值给 x 和 y 。 也可以直接使用:
[x,y] = solve(eq1,eq2,x,y)
进行求解,但需要注意,等式左边接收参数时应当按字母表进行排序,否则 MATLAB 不会自动识别你的参数顺序,比如:
[x,y] = solve(eq1,eq2,x,y)
[y,x] = solve(eq1,eq2,x,y)
solve 会把答案按字母表进行排序后进行赋值,x 解赋值给第一个参数,y 解赋值给第二个参数,对于第二种形式,实际上最终结果是变量 y 存储了 x 的解而变量 x 存储了 y 的解。 上述情况另一种解决方案是用下面的方法指定输出顺序:
syms x y
[y,x] = solve(x + y == 2,x - y == 1, [y, x])
由于是符号求解,有时候得到的解是一大串式子(符号求解无精度损失,所以 MATLAB 不会自动将答案转化为浮点数),这时候可以用 vpa 或者 double 函数将结果转换为单一的数,但需要注意的是 double 的结果为浮点数,vpa 的结果仍然是符号类型(即 sym 类型)。

另外,很多人习惯对于 solve 的参数采用字符型输入,这种方式有几个弊端。
首先就是程序的调试,一旦式子输入有误(最常见的就是括号的匹配),则调试起来会非常困难,例如:
solve('10^(-4.74)*0.965*y/60000x/(10^(-4.74)+x)+0.1/36500+10^(-14)/x-x=0','10^(-3.2)*x+0.333/3000+8*10^((-3.2)*0.1+0.1/333*y','x','y')
这时要去寻找式子输入错误会是一件很麻烦的事,MATLAB 也不会报告具体出错的地方。如果采用符号变量输入:
syms x y
eq1=10^(-4.74)*0.965*y/60000x/(10^(-4.74)+x)+0.1/36500+10^(-14)/x-x;
eq2=10^(-3.2)*x+0.333/3000+8*10^((-3.2)*0.1+0.1/333*y;
sol=solve(eq1,eq2,x,y)
会给程序的调试带来许多便利。对于某些错误,MATLAB 会给出错误代码颜色的高亮, 命令行还能返回具体的错误信息。并且采用字符型输入时,变量的赋值不能传入方程:
%例:x+y*sin(x) = 1

y = 1;
sol = solve('x+y*sin(x)=1','x')
MATLAB 会返回一个空解,而 sym 型输入:
syms x 
y = 1;
eq = x+y*sin(x)-1;
sol = solve(eq,x)
能够得到 sol = 0.51097342938856910952001397114508,其中的区别就在于 char 型输入尽管在 solve 前对 y 有一个赋值,但 solve 求解时依然会将 y 当作一个未赋值的常数。
最后,在今后的高版本 solve 将不支持 char 型参数输入,因此应该尽量放弃使用这种方法。

论坛关于 solve 的问题:
http://www.ilovematlab.cn/thread-434328-1-1.html
http://www.ilovematlab.cn/thread-436167-1-1.html
http://www.ilovematlab.cn/thread-281433-1-1.html
http://www.ilovematlab.cn/thread-268347-1-1.html

2. fzero

很多情况下, solve 并不能求得方程的解析解,这时可以采用数值法求解。
数值求解法包括 fzero 和 fsolve,其区别在于 fzero 只适用于求解一元函数零点,而 fsolve 适用于求解多元函数零点(包括一元函数)。
当求解一元函数零点时,推荐优先使用 fzero,原因是 fzero 求解一元方程往往更容易,它不仅支持提供初值的搜索,还支持在一个区间上进行搜索。

fzero 的常用形式:
x = fzero(fun,x0)
[x,fval] = fzero(fun,x0)
其中 fun 为函数句柄, x0 为搜索初值, fval 为求解误差。
%例:一元方程 sin(x)+cos(x)^2 = 0

y = @(x)sin(x)+cos(x).^2;   
[x,fval] = fzero(y,1)  %1为搜索初值
若方程有多个零点,fzero 只能根据你提供的初值求得最靠近初值的一个零点,如果希望求得多个零点,只能够通过改变初值来得到不同的零点。
对于初值的选取,目前来说没有什么比较好的办法,只能够通过分析方程的性质,或者通过作图的方法去寻找一个比较靠近零点的初值。另外,fzero 能够提供区间搜索,注意区间两端的端点函数值符号需要反向:
y = @(x)sin(x)+cos(x).^2;  
[x,fval] = fzero(y,[-1 1])     %fzero在[-1,1]这个区间进行搜索
建议尽量用区间搜索的方式来求解,因为这种方法比单纯的提供一个初始值的运算速度要快一些。而且新版本的 MATLAB 中关于此函数还有多个参数的形式,读者可以参考相关的 help。

除此之外,fzero 还能够求解积分方程:
http://www.ilovematlab.cn/thread-333673-1-1.html
http://www.ilovematlab.cn/thread-439192-1-1.html

论坛关于 fzero 的问题:
http://www.ilovematlab.cn/thread-434434-1-1.html
http://www.ilovematlab.cn/thread-319218-1-1.html

3. fsolve(Optimization Toolbox)

fsolve 可以求解多元方程,用法和 fzero 类似。 fsolve 的常用形式:
x = fsolve(fun,x0)
[x,fval] = fsolve(fun,x0)
其中 fun 为函数句柄, x0 为搜索初值, fval 为求解误差。
%例:求解方程组 x+y = 1, x-11y = 5

eq = @(x)[x(1)+x(2)-1;x(1)-11*x(2)-5];
[sol,fval] = fsolve(eq,[1,1])
这里对于方程的的输入需要采用矩阵的形式,其中 x(1) 代表 x , x(2) 代表 y 。有时候变量较多时可能会容易混淆,这里提供另一种方法,用符号变量表达方程再利用 matlabFunction 转化为函数句柄:
syms x y
eq1 = x+y-1;
eq2 = x-11*y-5;
eq1 = matlabFunction(eq1);   %将符号函数转化为函数句柄
eq2 = matlabFunction(eq2);
eq = @(x)[eq1(x(1),x(2)); eq2(x(1),x(2))];
[sol,fval] = fsolve(eq,[1,1])
结果与之前相同,但不容易出错。求得的解以矩阵形式返回给 sol ,即 sol 的第一个值求解的是 x(1) ,sol 的第二个值求解的是 x(2) 。
fsolve 要求求解的函数必须是连续的,而且成功求解时,fsolve 只能给出一组根。缺省情况下,trust-region dogleg 算法只能用于系统方程是方阵的情况,而 Levenberg-Marquardt 算法没有此限制。
新版本的 MATLAB 中关于此函数还有多个参数的形式,读者可以参考相关的 help。

论坛关于 fslove 的问题:
http://www.ilovematlab.cn/thread-438375-2-1.html
http://www.ilovematlab.cn/thread-274668-1-1.html
http://www.ilovematlab.cn/thread-273545-1-1.html
http://www.ilovematlab.cn/thread-320750-1-1.html

4. vpasolve(Symbolic Math Toolbox)

最后再补充一个数值解法 vpasolve,vpasolve 是 R2012b 引进的函数,可以求解一元或多元函数零点。相比于 fzero 和 fsolve 来说,vpasolve 最大的一个优点就是不需要提供初值,并且能够自动搜索指定范围内的多个解。

vpasolve 调用形式:
S = vpasolve(eqn) 
S = vpasolve(eqn,var) 
S = vpasolve(eqn,var,init_guess)
___ = vpasolve(___,Name,Value)
其中 eqn 是符号方程,var 为待求解变量,也可以不提供(第一种调用形式,默认求解变量由 symvar(eqn) 求得), init_guess 为搜索初值,Name,Value 为选项控制。
%例:对于多项式方程,vpasolve 能够给出所有解

syms x
vpasolve(4*x^4 + 3*x^3 + 2*x^2 + x + 5 == 0, x)

ans =
 
 - 0.88011 - 0.76332i
   0.50511 + 0.81599i
   0.50511 - 0.81599i
 - 0.88011 + 0.76332i
对于非多项式方程,vpasolve 只能给出一个解:
syms x
vpasolve(sin(x^2) == 1/2, x)

ans =
 
-226.94
这时可以提供搜索初值,来搜寻其它零点:
syms x
vpasolve(sin(x^2) == 1/2, x,100)

ans =
 
99.996
可以指定搜索范围,但不同于 solve,solve 指定求解范围是用 assume 函数,vpasolve 则是直接在输入参数中指定:
syms x
vpasolve(x^8 - x^2 == 3, x, [-Inf Inf]) %实数范围内求解
最后,vpasolve 一个很强大的用法,将 ‘random’ 选项设置为 true 可以直接搜索指定范围内不同解:
syms x 
f = x-tan(x);
for n = 1:3 
  vpasolve(f,x,'random',true) 
end

求解积分

求解积分与求解方程相同,也有两种方法,符号求解和数值求解。

1. int(Symbolic Math Toolbox)

int 是符号积分求解器,调用形式简单,但是功能非常强大。

int 常用形式:
int(expr,var)   %不指定积分上下限,求解不定积分
int(expr,var,a,b)   %指定积分上下限,求解定积分 
例:求解不定积分
syms x
f = 5/((x-1)*(x-2)*(x-3))
F = int(f,x)
例:求解定积分
syms x y; 
f = x/(1 + y^2)
F = int(f,y,0,1)
有时需要指定变量范围再进行求解:
例:求解不定积分
syms x a
assume(a ~= -1)
f = x^a;
F = int(f,x)
但是大多情况下 int 都得不到解析解,这时候就可以采用数值积分。

2. quad/quadl/quadgk/quadv

MATLAB 在 R2012a 版本引入了 integral,完全可以替代 quad/quadl/quadv,并且在以后的高版本中,MATLAB 将移除这3个函数,所以如果你的 MATLAB 版本高于 R2012a 的话,建议直接使用 integral

这4个函数都是数值积分函数,调用形式完全相同,只是分别适用于不同积分函数对象。其中:
  • quad 采用自适应 simpson 公式数值积分,适用于精度要求低,被积函数平滑性较差的数值积分;
  • quadl 采用自适应 Lobatto 数值积分,适用于精度要求高,被积函数曲线比较平滑的数值积分;
  • quadgk 采用自适应 Gauss-Kronrod 数值积分,适用于高精度和震荡数值积分,支持无穷区间,并且能够处理端点包含奇点的情况,同时还支持沿着不连续函数积分,复数域线性路径的围道积分法;
  • quadv 与 quad 算法相同,是 quad 的向量化版本,能够一次性计算多个积分。
应当注意,如果要采用数值积分计算一重积分的话,积分函数除了积分变量外,其它的参数都应当具有确定的数值。
调用形式以 quad 为例:
q = quad(fun,a,b)
q = quad(fun,a,b,tol)
其中 fun 为函数句柄, a 为积分下限,b 为积分上限,tol 为积分精度,默认为1e-6。
例:计算
y = @(x)1./(x.^3-2*x-5);
q = quad(y,0,2)
例:计算
y = @(x)exp(-x.^2);
q = quadgk(y,0,inf)

对于 quadv 向量化积分,可以参考@winner245版主的帖子:http://www.ilovematlab.cn/thread-265346-1-1.html

3. integral

integral 是 R2012a 引进的一个函数,一元函数数值积分中功能最为强大,调用形式和 quad 基本一致:
q = integral(fun,xmin,xmax)
q = integral(fun,xmin,xmax,Name,Value)
其中 fun 为函数句柄, xmin 为积分下限, xmax 为积分上限,Name 和 Value 是选项控制,包括误差控制、向量化积分设置等等。
integral  配合 fzero 可以求解无法显式表达的函数的定积分:
已知,求解
q = @(k,w)w.^2/10.*coth(30*k)-k;
v = @(w)fzero(@(k)q(k,w),1e3);   %利用fzero求解k,相当于显式表达k
integral(v,0,10,'ArrayValued',1)
论坛关于数值积分的问题:
http://www.ilovematlab.cn/thread-438090-1-1.html
http://www.ilovematlab.cn/thread-275347-1-1.html
http://www.ilovematlab.cn/thread-319403-1-1.html

4. trapz

trapz 是基于梯形法则的离散点积分函数。 调用形式:
I = trapz(x,y)
其中 x 和 y 分别是自变量和对应函数值,以 sin(x) 在 [0,pi] 积分为例:
x = linspace(0,pi,1e3);     %生成 [0,pi] 内的一系列离散点
y = sin(x);
I = trapz(x,y)
论坛关于trapz的问题:
http://www.ilovematlab.cn/thread-440457-1-1.html
http://www.ilovematlab.cn/thread-439162-1-1.html

浮点数误差

由于计算机中都是以二进制形式存储数据,当用十进制数进行计算时,就会存在十进制数二进制数的转换。但是某些十进制数转化为二进制数是一个无限位的小数,这时必须对该无限位小数进行截断计算机才能存储,那么此时就会产生误差,即浮点数误差。
例如十进制的0.9,在转化为二进制时是无限循环小数0.1110011001100110011...。此时须对该无限位小数进行截断才能保存在内存当中,截断后再转换回十进制时,0.9就变成了0.90000000000000002,这就是浮点数误差的产生过程。
由于浮点数误差的存在,当进行数值计算时就会出现一些不可避免的问题,最常见的就是判断两数相等时得到与预期相反的结果。
例:令 a = 0.1+0.2, b = 0.3, 判断 a==b 时,MATLAB 会返回0, 当执行 a-b 时,会发现结果不是精确等于0,而是一个非常小的数5.5511e-17。
或者在矩阵中寻找数的位置(也相当于是判断两数相等)。
%例
>> a = 0.1:0.1:0.5

a =

    0.1000    0.2000    0.3000    0.4000    0.5000

>> find(a==0.3)

ans =

   Empty matrix: 1-by-0
由于 a 向量中的 0.3 是由 0.1+0.1+0.1 计算得到,在计算过程中就产生了浮点数误差,这也导致在判断 a==0.3 时结果为false,所以 find(a==0.3) 返回一个空矩阵。
在进行数值计算判断两数相等时,最好不要直接判断,而是设立一个容差值,当两个浮点数的差的绝对值小于给定的容差值时,我们就认为这两个浮点数相等。
比如对于上面的例子:
>> a=0.1:0.1:0.5

a =

0.1000 0.2000 0.3000 0.4000 0.5000

>> tol=eps(0.3)*10 %设立容差值,一般比这个点的浮点数误差高一到两个数量级即可。eps函数能够求得该点的浮点数误差值。
tol =
5.5511e-15
 
>> find(abs(a-0.3) ans =
3

论坛关于浮点数误差的问题:
http://www.ilovematlab.cn/thread-439162-1-1.html
http://www.ilovematlab.cn/thread-333109-1-1.html
http://www.ilovematlab.cn/thread-262078-1-1.html

生成一系列有规律名变量

当循环迭代需要把每次迭代结果进行保存时,如果每次迭代的结果是尺寸不同的矩阵,无法用矩阵进行存储,那么可以利用 eval 和 num2str 这两个函数可以生成一系列例如 a1、a2、a3… 变量对结果进行保存(不推荐这种方法,原因是 eval 这个函数有很多缺点)。
  • eval::将括号内的字符串视为语句并运行。
  • num2str: 将数值转换为字符串。
%例
for ii=1:10
    str=['a',num2str(ii),'=1:',num2str(ii)];
    eval(str)
end
这样可以生成一系列变量 a1、a2…a10 对循环结果进行保存。
不推荐使用 eval 函数的原因,帮助文档有详细的解释。
  • MATLAB® compiles code the first time you run it to enhance performance for future runs. However, because code in an eval statement can change at run time, it is not compiled.
  • Code within an eval statement can unexpectedly create or assign to a variable already in the current workspace, overwriting existing data.
  • Concatenating strings within an eval statement is often difficult to read. Other language constructs can simplify the syntax in your code.
MATLAB 对于这类问题有更好的解决办法,利用元胞数组对结果进行存储。元胞数组是 MATLAB 中的特色数据类型,它的元素可以是任意类型的变量,包括不同尺寸或不同维度的矩阵。 对于上面的例子,利用元胞数组:
for ii=1:10
    a{ii}=1:ii;
end
即生成一系列元胞存储循环结果。这样无论是程序的可读性、运行效率还是后续程序对保存结果调用的方便程度,都远胜于 eval 函数。
除此之外,在处理符号变量时如果需要生成一系列有规律名符号变量,例如生成一个多项式:
y = x1+2*x2+3*x3+…+100*x100
eval+num2str 能够实现,但更简便的方法还是利用矩阵:
x=sym('x',[1,100]);
w=(1:100).*x;
y=sum(w)

论坛关于生成系列变量问题:
http://www.ilovematlab.cn/thread-435630-1-1.html
http://www.ilovematlab.cn/thread-272264-1-1.html
http://www.ilovematlab.cn/thread-437236-1-1.html

统计向量中连续出现的数字并计数

例如对于一个行向量 [0 0 0 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1],我想要统计其中连续1的位置以及连续出现的次数,有下面两种方法:

1. 利用 diff 函数

diff: 求前后两项之差,diff(X)= [X(2)-X(1),X(3)-X(2),...,X(n)-X(n-1)]。

A = [0 0 0 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1];
k = diff([0 A 0])           %对A前后补0之后再使用diff,补0是为了保证对于A的第一个和最后一个元素是1的情况,也能够通过diff求得1或-1,然后再根据1和-1来寻找连续1的位置和个数
ind = find(k==1)            %1出现的位置即连续1出现的位置
num = find(k==-1)-ind       %-1和1出现的位置差即连续1出现的个数
其中 ind 是出现连续1的首尾的索引,num 是该连续1出现的个数。这里 ind = [4 8 12 18], num = [3 2 3 1],也就是说 A 向量中4这个位置开始出现连续1,连续出现3次。同理8位置开始出现连续1,连续出现2次。

2. 转换成字符串之后用正则表达式进行匹配

A = [0 0 0 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1];
Ach = num2str(A); Ach(Ach==' ') = [];            %将A转换为字符串
[ind,indEnd] = regexp(Ach,'1*','start','end');    %匹配字符串连续1的位置
num = indEnd-ind+1;
很多这种统计向量中数的问题都可以转化为检测连续1出现次数,例如统计向量A中连续数字出现的位置和次数,A = [21,23,25,27,28,29,30,31,33,35,36,38,47,55], 那么进行一次转换:k = diff(A)==1,就可以得到 k  =  [0 0 0 1 1 1 1 0 0 1 0 0 0],其中 k 中连续1出现的位置是 A 中连续数字出现的位置,k中连续1出现的次数加1就是 A 中连续数字出现的次数。

论坛关于统计连续1个数问题:
http://www.ilovematlab.cn/thread-176813-1-1.html
http://www.ilovematlab.cn/thread-440131-1-1.html
http://www.ilovematlab.cn/thread-440520-1-1.html

读取文本文件

文本文件的读写函数可以分为两类,一类是高级函数(high-level),一类是底层函数(low-level)。通常认为高级函数运用起来简单,但是定制性差。底层函数用法复杂,但是灵活性高。由于 MATLAB 提供了许多可以读取文本文件的函数,例如 load、importdata、textread、dlmread、csvread,要把这些函数各自的适用范围弄清楚也不是一件容易的事,我的建议是掌握两个底层函数 fscanf 和 textscan 的用法,这样就能够轻松应对一般文本文件的读取了。下面简单介绍几个高级函数的用法,着重介绍两个底层函数 fscanf 和 textscan 的用法。

1. load/importdata/csvread

对于简单文本文件的读取(文件内容只包括数值,并且以逗号或空格为分隔符),这三个函数的常见用法基本一致。
load、importdata、csvread 常见用法:
M = csvread(filename)
M = load(filename)
M = importdata(filename)
filename 即你需要读取的文件。

例如,创建一个文件,test.txt,包含以下数据:
16,2,3,13
5,11,10,8
9,7,6,12
4,14,15,1
读取整个文件:
filename = test.txt;
M = csvread(filename)
% M = load(filename)
% M = importadata(filename)
M =
    16     2     3    13
     5    11    10     8
     9     7     6    12
     4    14    15     1

2. dlmread

dlmread 的用法比 csvread 稍微复杂一点,它能够指定分隔符(csvread 只能读取逗号分隔符和空格分隔符)。
dlmread 常见用法:
M = dlmread(filename)
M = dlmread(filename, delimiter)
其中 filename 为所读取的文件,delimiter 为分隔符。

例:对于包含以下内容的文本文件:
16。2。3。13
5。11。10。8
9。7。6。12
4。14。15。1
就可以指定’。’为分隔符进行读取:
filename = 'csvlist.dat';
M = dlmread(filename,’。’)
M =
    16     2     3    13
     5    11    10     8
     9     7     6    12
     4    14    15     1
如果行列数不一致的数据, dlmread 会自动在空白数据处补0。

例:对于包含以下内容的文本文件:
40   5     30   1.6   0.2   1.2
15   25   35   0.6   1      1.4
20   45   10   0.8   1.8   0.4

2.6667   0.33333   2
1           1.6667     2.3333
1.3333   3             0.66667
filename = 'csvlist.dat';
M = dlmread(filename)
M = 
    40.0000    5.0000   30.0000    1.6000    0.2000    1.2000
    15.0000   25.0000   35.0000    0.6000    1.0000    1.4000
    20.0000   45.0000   10.0000    0.8000    1.8000    0.4000
    2.6667    0.3333    2.0000         0         0         0
    1.0000    1.6667    2.3333         0         0         0
    1.3333    3.0000    0.6667         0         0         0

3. fscanf

按指定格式从文本文件中读取数据。用法:
A = fscanf(fileID,formatSpec); 
%通过指定读取格式formatSpec,从文本文件中读取数据至列向量A。fscanf会重复应用格式字符串formatSpec,直到文件指针到达文件末尾,如果读取到不能匹配formatSpec的数据则读取自动结束。
A = fscanf(fileID,formatSpec,sizeA);
%sizeA能够指定读取数据的大小,当读取到sizeA大小的数据时,文件指针会停止,读取结束。注意fscanf读取的是列主序,通常读取完还需要进行转置操作。
所要读取的文本文件被文件标识符 fileID 标识,通过 fopen 函数可以获取文件的 fileID。当结束读取时,一定要记得使用 fclose 函数关闭文件。
光看函数的用法介绍可能会比较难懂,通过下面的例子会比较容易理解。 例:文本文件 test.txt 包含以下数据:
16。2。3。13
5。11。10。8
9。7。6。12
4。14。15。1
fid = fopen('test.txt');              %通过fopen获取文件标识
formatSpec = '%d。%d。%d。%d';         %指定读取格式
A = fscanf(fid,formatSpec ,[4,4]);    %读取文件数据并存为4*4矩阵
fclose(fid);                          %调用fclose关闭文件
A = A.’                               %由于fscanf是列主序,因此读取完还需要进行转置

A =
    16     2     3    13
     5    11    10     8
     9     7     6    12
     4    14    15     1
下面详细解释一下 fscanf 的读取原理:
当用 fopen 打开文件时,会有一个文件指针在文件开头。fscanf 通过你设定的格式字符串 formatSpec 来读取数据(formatSpec 由字符串和转义说明符组成,其中转义说明符由 % 开头,以转换字母结尾。上面的例子中 %d 就是一个转义说明符,代表一个整数,常用的还有%f、%s,分别代表浮点数和字符串)。formatSpec 第一个字符块为转义说明符 %d,那么 fscanf 会先将第一个整数16读入进 A,之后文件指针跳至16右边,formatSpec 第2个字符块是字符串’。’,由于它不是转义说明符,文件指针会跳过’。’到达’。’右边。之后再是转义说明符 %d,则将2读入进 A,以此类推。
用下面图片进行说明:

如果将这个例子的读取代码写成:
fid = fopen('test.txt');
A = fscanf(fid,'%d。',[4,4])
fclose(fid);
A = A.'
将会得到:
A =
    16     2     3    13
原因就是当文件指针读取完13时,formatSpec 需要匹配的字符串是’。’,但是13的下一个字符串是5,匹配失败,fscanf 停止读取。
再以一个比较复杂的文本文件为例:
例:文本文件test.txt包含以下数据:
lambda: 7.580000e-05
lambdaB: 8.000000e-05
initial pulse width: 7.853636e-13
output pulse width: 6.253030e-13
dispersion length: 6.307732e-02
nonlinear length: 9.572495e-01

lambda: 7.590000e-05
lambdaB: 8.000000e-05
initial pulse width: 7.848788e-13
output pulse width: 5.778485e-13
dispersion length: 5.852858e-02
nonlinear length: 9.195277e-01


现在想要把所有的数字信息提取出来:
fid = fopen('F:\test.txt');
c1 = '%*s %e';                       %第一行的转义说明符,’%’后面接一个’*’代表跳过这个数据,%*s即代表跳过第一个字符串’lambda:’,%e表示读取以科学计数法表示的数字。
c2 = '%*s %e';
c3 = '%*s %*s %*s %e';
c4 = '%*s %*s %*s %e';
c5 = '%*s %*s %e';
c6 = '%*s %*s %e';
formatSpec = [c1,c2,c3,c4,c5,c6];    %以6行为一组,重复读取,直至读取完整个文件
A = fscanf(fid,formatSpec,[6,inf])
fclose(fid);

4. textscan

textscan 的用法与 fscanf 类似,建议先将 fscanf 的用法弄清楚再来看 textscan。

textscan 常见用法:
C = textscan(fileID,formatSpec)
C = textscan(fileID,formatSpec,N)
同 fscanf 一样,fileID 为文件标识符,formatSpec 为格式字符串。N 则是重复匹配formatSpec 的次数。
与 fscanf 不同的是, textscan 将每个与 formatSpec 转义说明符匹配出来的数据都用一个元胞进行存储。并且 textscan 有很多选项提供,比如 ’Headerlines’ ,可以指定跳过文件的前n行; ’Delimiter’ 可以指定分隔符等等。

例:文本文件test.txt包含以下数据:
16。2。3。13
5。11。10。8
9。7。6。12
4。14。15。1
fid = fopen('F:\test.txt');
formatSpec = '%d'
A = textscan(fid,formatSpec,'delimiter','。');           %指定’。’为分隔符,如果不指定分隔符的话,就需要把formatSpec写成'%d。%d。%d。%d' 。
fclose(fid);
celldisp(A)

A{1} =
          16
           2
           3
          13
           5
          11
          10
           8
           9
           7
           6
          12
           4
          14
          15
           1

关于作者

wuyou136,MATLAB 中文论坛基础讨论版块超级活跃用户。2014年10月加入 MATLAB 中文论坛以来,回答问题3000余次,获得最佳答案596个,在论坛排行榜排名第8,实力派论坛“神”级大牛之一。中山大学光学工程系在读研究生,现阶段研究液晶光子晶体非线性效应的数值模拟,本科毕业于天津大学光电子技术科学系。

声明:
本文内容所有内容仅代表个人观点,如有任何问题,请联系作者。
本版块所有文章版权归作者个人所有,未经允许,不得作为出版物出版。如需转载,请联系论坛管理员

75

鲜花
1

握手

雷人
1

路过
1

鸡蛋

刚表态过的朋友 (78 人)

相关阅读

发表评论

最新评论

引用 风之子儿 2017-9-29 08:00
好东西啊,以前都没怎么发现。楼主给力
引用 自行车111 2017-9-25 20:56
总结很全面
引用 悟得 2017-9-24 19:28
总结的非常好,论坛应该将这些基础性的东西以一定的顺序收集起来,现在还是有很多入门者提问论坛上总结过的问题,说明他们在搜索答案的时候还是有障碍。 ...
引用 shankegaoyang 2017-8-16 20:20
NB
引用 周大帅哈哈 2017-8-9 10:06
太棒了  完美解决的之前被solve卡住的问题!!!!!!谢谢作者
引用 123小丑皇 2017-5-10 10:41
完了....因为士兵图片...谢谢作者,很受用
引用 mouron 2017-5-1 21:29
写得真详细!!!谢谢您!!!
引用 eeeeros 2017-4-5 20:48
差点以为煎鸡蛋是吃的,幸好没有点下去。。。
引用 靳洋 2017-4-2 14:42
太棒了!
引用 童霞之恋 2017-3-26 10:32
太牛了,解决了我一大问题
引用 1477995337 2017-3-22 18:18
谢谢走总结很好,很受用
引用 wuyou136 2016-12-29 09:26
对的,谢谢提醒。那里应该是输入代码的时候出错了,正确的应该是 find(abs(a-0.3)<tol)
引用 jiesi007 2016-12-28 10:39
楼主,那个浮点误差的那个最后没写完吧应该是 find(abs(a-0.3)<tol)吧
引用 123xms 2016-12-8 15:36
太赞了,刚好能解决我的问题,谢谢啦!
引用 xuxuexing100 2016-11-24 16:09
作者真是太好了,为我等小白提供巨大的便利,谢谢!
引用 qibbxxt 2016-11-5 15:48
关于浮点数的存储,应该是用IEEE754的标准(或者其他的格式标准),而非直接把十进制数转化成二进制数

查看全部评论(13)

MATLAB中文论坛常见问题归纳

这里总结归纳了一些论坛中经常重复常见的一些MATLAB问题。一方面可以避免作重复性回答,另一方面为 MATLAB 初学者提供一些常见问题的解决办法,希望对大家有所帮助。 ... ... ... ...

MATLAB中文论坛常见问题归纳

这里总结归纳了一些论坛中经常重复常见的一些MATLAB问题。一方面可以避免作重复性回答,另一方面为 MATLAB 初学者提供一些常见问题的解决办法,希望对大家有所帮助。 ... ... ... ...
关闭

站长推荐上一条 /2 下一条

返回顶部