【轉載】Perl異常處理方法總結

來源:互聯網
上載者:User

標籤:

    程式指令碼在運行過程中,總會碰到這樣那樣的問題,我們會預知一些問題並為其準備好處理代碼,而有一些不能預知。好的程式要能儘可能多的處理可能出現的異常問題,本文就總結了一些方法來解決這些異常,當然perl在這個處理了不及其它同類語言,但也不會差到那裡。在開始前,我們先盤點一些關於perl的優缺點。

0. 曆史太悠久了。你可以在1997年的電腦上找到perl5.0。(只是吐槽一下,曆史悠久沒什麼不好,與時俱進才是關鍵)

1. 不回收迴圈垃圾(這是個硬傷,也許和Perl設計的初衷有關,小指令碼影響不大;但因為這個,perl與稍微大一點的程式就無緣了)

2. 容錯(如字串和數值的隱式轉換等),但換句話說就是對錯誤過於放縱,程式產生錯誤結果而不是報錯。

3. 一件事有很多方法做(TIMTOWTDY),但換句話說就是做一件事沒有“一種明顯的做法”。比如數組的長度是$#somearray+1。為什麼是加一呢?難道不是somearray.length或者len(somearray)嗎?

4. 文法比較難懂。文法還處在“指令碼語言”(指的是那些隨手寫隨手扔的批處理指令碼)的定位上,只是稍微比shell指令碼更像傳統的程式設計語言。比如:
4.1 大量使用$符號,以及各種用符號表示的特殊變數。

4.2 函數不帶形參列表。

4.3 物件導向支援不好:文法中沒有對象文法(沒有class等關鍵字),全靠hash和bless。繼承靠ISA,也沒有文法支援。

4.4 只有基本的異常處理(perl的eval和die,類似java的try和throw)。但是異常只是一條字串資訊,少了基於對象的異常系統,異常處理總是不如其它語言。

以上這些都會影響可讀性和可寫性。需要注意的是,人的大腦同時可以關注的資訊是有限的。如果寫商務邏輯,卻偏偏要不斷關注實現細節(比如使用package, hash, ISA, bless來實現物件導向),程式員的思路就會不斷地被這些細節打亂,急劇降低編程速度,還會不斷犯錯誤,甚至為了簡單性而犧牲效率甚至正確性。 

一個進階的語言就是對進階的概念的抽象,使得程式員可以將思維從下層的細節中脫離出來(Separation of Concern)。Perl作為文本批處理用的指令碼語言是夠的,可以做很多shell不擅長的字串處理(Regex很贊)和計算,這在當時也是好的。但是對於稍微大一些的程式,甚至需要物件導向的時候,Perl就不合適了。 

Perl可以寫出很簡短的代碼(Google一下“code golf”),在這一點上Ruby更像Perl。下面就介紹一些讓Perl指令碼編寫的更加規範,甚至在出現錯誤時能得到很好的處理的方法技巧。

開啟約束指令,讓編碼更規範

如果你使用perl5.10 或更高的版本,可以顯示的指定當前Perl版本號碼來自動開啟約束指令。

use 5.012; #自動啟用use strict 指示詞
use v5.10; #自動布strict 模式

之前的版本通過添加下邊的指令:
use strict;

通過啟用約束指令,編程時常見的錯誤很容易暴露出來。而啟用warnings指令的話,還能捕獲一些其他不是很緊要的問題。

如果擔心原先的程式啟用strict後程式不正常運行,則可以在實際修改代碼前,先在命令列上試著啟用strict看看:
perl -Mstrict freeoa_program.pl

Perl的約束集包括vars(變數),subs(子程式)和refs(引用)這三部分,通常這三種約束同時使用。

在很多情況下,系統調用可能會失敗;例如,嘗試開啟不存在的檔案,或者刪除某個仍含有檔案的目錄,或者嘗試讀取沒有讀許可權的檔案。在前面的樣本中,我們已經用到了die函數,詳細討論有關錯誤處理和錯誤處理函數的相關內容。這些函數包括die函數、warn函數和eval函數。

die函數用於在命令或檔案控制代碼失敗時退出Perl指令碼。 

warn函數類似於die函數,但它不會退出指令碼。 

eval函數具有多種用途,但它主要還是用於異常處理。 

讀者想必還記得短路運算子&&和||,這兩個運算子首先會求其左側運算元的值,然後才會求其右側運算元的值。如果&&左側運算元值為true,則求其右側的運算元。如果||左側運算元的值為false,這才求其右側的運算元。

Perl 5提供的Carp模組擴充了die和warn的功能

對一段代碼中如果有一句出現異常,但事先並不知道是哪一句,怎樣進行異常的捕獲?

eval{.........};#捕獲運行時的錯誤

if([email protected]){........};#進行錯誤處理

通過使用 Perl 的 eval 語句來分析錯誤,使用標準方法來處理 Perl 錯誤,請使用以下文法:
eval {enter statements you want to monitor}; 

在運行時,如果 Perl 引擎在 eval 塊內的語句中遇到錯誤,那麼它會跳過 eval 塊的剩餘部分,並為對應的錯誤文本設定 [email protected]。例如:
eval{$objectName->MethodName();};
if ([email protected]){ 
 print "Error using MethodName method. Error: [email protected]\n"; 

else{
 # continue without error ... 
}

某些預期通常失敗函式不包含在此範圍內。尤其是驗證和設定欄位函數返回錯誤說明,而不是拋出異常。這樣可攔截內部中斷訊號後對其進行處理,而不是簡單地讓程式對異常按預設方法進行處理。

The recommended way to do some things, like timeouts, is through an eval/die pair. However, I‘ve noticed that if you set $SIG{__DIE__} to do some custom reporting, like in:
 $SIG{__WARN__} = sub { ... };   # custom error report
 $SIG{__DIE__} = sub { &{$SIG{__WARN__}; exit(1); };
 # custom error report and terminate

eval {
 $SIG{ALRM} = sub { die "timeout\n"; };
 alarm 20;
 ...
 alarm 0;
}

"die" isn‘t caught! It remains fatal!

The only way around this (that I found), is to clear $SIG{__DIE__} in the eval blok:
eval{
 local($SIG{__DIE__});   #back to standard Perl die
 ...
}

Question: is there another way to terminate a __DIE__ sub, that is NOT fatal in eval? "die" doesn‘t die in eval, but ONLY if you didn‘t set $SIG{__DIE__} yourself (even outside of the eval block). Surely, this can‘t be the way it‘s supposed to be? 

你最好要使用Try::Tiny模組來處理這些問題,這將協助你避免一些老的版本裡的陷阱。

use Try::Tiny;
try{
 die "foo";
}catch{
 warn "caught error: $_";
};

有時我們常常寫多個 open ,然後還要寫上多次 die 像下面,讀一個檔案,然後寫一個檔案,有時我寫的 open 會超過 4 個,要寫很多次 die 這時非常麻煩。我們如果直接 use autodie 就直接能解決所有問題,所有的 die 的地方都能自動實現。

open my $fh, ‘<‘, $in or die "$!";
open my $fh, ‘>‘, $out or die "$!";

用autodie簡化錯誤處理


autodie編譯指令(從5.10.1起開始內建,也可以直接從CPAN安裝)

預設情況下,autodie會對它能起作用的所有函數生效。如果只是希望對某些特定函數起作用,可以將各個函數的名字或一組函數的組名列出來告訴autodie:
use autodie qw(open close); #只對特定函數生效
use autodie qw(:filesys); #只對 某組函數生效

在autodie捕獲錯誤時,它會把[email protected]設定為autodie::exception對象,而[email protected]就是表示eval錯誤變數

加上 autodie 以後就成了
use autodie
open my $fh, ‘<‘, $fin;
open my $fh, ‘>‘, $fout;

有了這個方便多了,有時我們常常不想讓程式 die 了後退出,想抓到原因,得使用 eval 然後來檢查 [email protected],像下面這樣。 

use autodie;
eval{ 
 open my $fh, ‘<‘, $in; 
 # .....
}
if([email protected]){
 #TODO
}

eval{ 
 open my $fh, ‘>‘, $out;
}

if([email protected]){
 #TODO
}

還好上次只有二個 open 。是不是感覺很痛苦,要是 open 之類會 die 的更加多點的話。另外,有沒有發現,只要第一個 open 失敗,第二個 if ([email protected]) 永遠會失敗,因為 [email protected] 的標記下面也檢查同樣的變數。這時使用 Try::Tiny 中的 try catch 吧。

與Try::Tiny模組配合使用


Perl 沒有內建的異常處理機制,所以最合適的方法就是使用Try::Tiny模組。雖然CPAN中處理異常的模組很多,但是這個模組最為輕巧,使用起來也沒有過多的依賴關係。

use autodie;
use Try::Tiny;
# handle errors with a catch handler
try{
 die "foo";
}catch{
 warn "caught error: $_"; # not [email protected]
};

注意:catch 代碼以分號結尾的,是一個運算式。另外它會將出錯的資訊儲存在變數$_而不是[email protected]中。

這時使用很方便

use autodie;
use Try::Tiny;
try { 
 open my $fh, ‘<‘, $in; 
 # .....
 open my $fh, ‘>‘, $out;
 # .....
}
catch {
 print "Error $_\n";
}

有時我們還可以在這個地方使用‘try catch‘做程式流程式控制制
use autodie;
use Try::Tiny;

try { 
 &sub1();
 &sub2();
 &sub3();
}
catch {
 &suba();
 &subb();
 &subc();
}

像上面,只要在 sub1, sub2, sub3 任意一個子函數 die 出來,這個流程就認為出錯,就做其它的另一種方案的流程 suba,subb,subc。

【轉載】Perl異常處理方法總結

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.