MATLAB中文論壇 文章 技術專欄 MATLAB基礎 查看內容

奇迹觉醒玩什么职业好:MATLAB中文論壇常見問題歸納

奇迹觉醒女神之光 www.mhotr.icu 2019-3-3 14:48| 發布者: ilovematlab| 查看: 52940| 評論: 22|原作者: 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 的問題:
//www.mhotr.icu/thread-434328-1-1.html
//www.mhotr.icu/thread-436167-1-1.html
//www.mhotr.icu/thread-281433-1-1.html
//www.mhotr.icu/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 還能夠求解積分方程:
//www.mhotr.icu/thread-333673-1-1.html
//www.mhotr.icu/thread-439192-1-1.html

論壇關于 fzero 的問題:
//www.mhotr.icu/thread-434434-1-1.html
//www.mhotr.icu/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 的問題:
//www.mhotr.icu/thread-438375-2-1.html
//www.mhotr.icu/thread-274668-1-1.html
//www.mhotr.icu/thread-273545-1-1.html
//www.mhotr.icu/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版主的帖子://www.mhotr.icu/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)
論壇關于數值積分的問題:
//www.mhotr.icu/thread-438090-1-1.html
//www.mhotr.icu/thread-275347-1-1.html
//www.mhotr.icu/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的問題:
//www.mhotr.icu/thread-440457-1-1.html
//www.mhotr.icu/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

論壇關于浮點數誤差的問題:
//www.mhotr.icu/thread-439162-1-1.html
//www.mhotr.icu/thread-333109-1-1.html
//www.mhotr.icu/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)

論壇關于生成系列變量問題:
//www.mhotr.icu/thread-435630-1-1.html
//www.mhotr.icu/thread-272264-1-1.html
//www.mhotr.icu/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個數問題:
//www.mhotr.icu/thread-176813-1-1.html
//www.mhotr.icu/thread-440131-1-1.html
//www.mhotr.icu/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,實力派論壇“神”級大牛之一。中山大學光學工程系在讀研究生,現階段研究液晶光子晶體非線性效應的數值模擬,本科畢業于天津大學光電子技術科學系。

聲明:
本文內容所有內容僅代表個人觀點,如有任何問題,請聯系作者。
本版塊所有文章版權歸作者個人所有,未經允許,不得作為出版物出版。如需轉載,請聯系論壇管理員。

相關閱讀

發表評論

最新評論

引用 王維強2 2019-8-27 16:11
謝謝樓主,費心啦!
引用 我只是一知菜鳥 2019-8-20 11:24
非常好用,適合初學者,謝謝樓主
引用 晚鐘過雨 2019-8-17 15:15
謝謝樓主,給你點贊
引用 shiyunbuji 2019-8-13 15:54
總結的非常棒
引用 m_dgz 2019-8-10 17:57
樓主列舉的很細,謝謝分享
引用 王闖_RaIRa 2019-8-9 20:05
寫的真的很詳細,特別適合新手~
引用 breezy_gkpm4 2019-8-3 22:09
知道了
引用 wspytu 2019-7-15 17:37
Mark一下,講解的好詳細
引用 a42826228 2019-6-28 02:03
謝謝樓主,給你點贊
引用 替天行道zzh 2019-6-4 14:15
謝謝,寫的真好
引用 我是高冷心 2019-5-13 18:49
挺不錯的,謝謝!
引用 guojiangtao 2019-4-29 22:21
挺不錯的,感謝~
引用 土木小王子 2019-4-25 14:41
總結的非常棒,特別是文件讀取部分對我特別有用!感謝!
引用 陳昆燦 2019-4-18 15:37
重拾大學記憶,這貼給力!
引用 誠實的亞伯 2019-3-26 09:59
非常不錯的帖子,學習到了基本知識
引用 皮皮小龍蝦 2019-3-18 22:50
謝謝樓主分享
引用 nibonibo 2019-3-9 18:50
包含很全面,值得收藏。學習
引用 a78307841 2019-3-4 15:24
非常實用的帖子,感謝樓主分享
引用 18280124054 2019-2-26 20:10
讀取文本 : textread xlsread ...
保存文檔 : save fprintf ...
樓主有時間可以補充一下
引用 wangchao915 2019-2-22 09:23
感謝

查看全部評論(22)

MATLAB中文論壇常見問題歸納

這里總結歸納了一些論壇中經常重復常見的一些MATLAB問題。一方面可以避免作重復性回答,另一方面為 MATLAB 初學者提供一些常見問題的解決辦法,希望對大家有所幫助。 ... ... ... ... ... ... ...

MATLAB中文論壇常見問題歸納

這里總結歸納了一些論壇中經常重復常見的一些MATLAB問題。一方面可以避免作重復性回答,另一方面為 MATLAB 初學者提供一些常見問題的解決辦法,希望對大家有所幫助。 ... ... ... ... ... ... ...
關閉

站長推薦上一條 /4 下一條

返回頂部