1. 程式人生 > >Perl異常處理方法總結

Perl異常處理方法總結

程式指令碼在執行過程中,總會碰到這樣那樣的問題,我們會預知一些問題併為其準備好處理程式碼,而有一些不能預知。好的程式要能儘可能多的處理可能出現的異常問題,本文就總結了一些方法來解決這些異常,當然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的evaldie,類似java的try和throw)。但是異常只是一條字串資訊,少了基於物件的異常系統,異常處理總是不如其它語言。 以上這些都會影響可讀性和可寫性。需要注意的是,人的大腦同時可以關注的資訊是有限的。如果寫業務邏輯,卻偏偏要不斷關注實現細節(比如使用package, hash, ISA, bless來實現面向物件),程式設計師的思路就會不斷地被這些細節打亂,急劇降低程式設計速度,還會不斷犯錯誤,甚至為了簡單性而犧牲效率甚至正確性。 一個高階的語言就是對高階的概念的抽象,使得程式設計師可以將思維從下層的細節中脫離出來(Separation of Concern)。Perl作為文字批處理用的指令碼語言是夠的,可以做很多shell不擅長的字串處理(正則表示式很贊)和計算,這在當時也是好的。但是對於稍微大一些的程式,甚至需要面向物件的時候,Perl就不合適了。 Perl可以寫出很簡短的程式碼(谷歌一下“code golf”),在這一點上Ruby更像Perl。下面就介紹一些讓Perl指令碼編寫的更加規範,甚至在出現錯誤時能得到很好的處理的方法技巧。 開啟約束指令,讓編碼更規範 如果你使用perl5.10
或更高的版本,可以顯示的指定當前Perl版本號來自動開啟約束指令。 use 5.012; #自動啟用use strict 指令 use v5.10; #自動布嚴格模式 之前的版本通過新增下邊的指令: 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模組擴充套件了diewarn的功能 對一段程式碼中如果有一句出現異常,但事先並不知道是哪一句,怎樣進行異常的捕獲? 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。 原文來自:http://www.freeoa.net/development/perl/perl-exception-handling-methods_2679.html