緣由
最近反過頭來看Perl,一方面是我的客戶廠家用Perl開發了很多東西,另一方面在瞭解過程中,又複習了Perl語言,並且有了一些心得。記錄下來,供大家參考。
我在做一個小程式,就是解析一個文字檔,將其中的內容進行處理,並儲存在另一個檔案。
背景知識
1. Perl的對象化編程實現
請參考閱讀:
O'Reilly - Advanced Perl Programming.pdf - 第7章 [Object-Oriented Programming]
2. 在Perl中如何使用引用
http://www.chinaunix.net/jh/25/504623.html
3. Perl中如何讀取檔案
Perl cookbook [O'reilly Perl Cookbook.pdf] - 第7章 [File Access]
4. Perl的Regex
http://www.chinaunix.net/jh/25/159388.html
具體實現
對象編程是為瞭解決長期依賴困擾的維護和擴充的工作。如果我們的代碼唯寫一次,以後再也不需要改動,那麼也許現代編程模型就不會演化。當然我們知道這是不可能的。世界唯一不變就是變化。
Perl做為一種功能強大的指令碼語言在各個方面都有運用(但沒有在UI方面使用),但是由於指令碼語言的特性,write once的標籤一直伴隨著它。大家普遍認為它是一種晦澀的並難以維護的程式語言。
其實這方面,大家有一些誤解。語言使用跟開發人員的水平和涵養有關係,即便在C#這種良好的進階對象語言的基礎上,部分開發人員仍然可以寫糟糕的難以讀懂的語言結構來。因此,開發人員不僅僅是要學會在進階語言上使用對象編程,並更需要深刻理解對象概念,最終能將這種概念運用到不同的語言中來,使其語言結構,模組化,對象化。
在我剛剛接觸Perl的時候,我認為這種語言根本無法寫出對象化的語言結構。因此,我對此有諸多抱怨。但隨著學習的深入,Perl一樣可以使用對象的概念來構件合適和清晰的語言結構。
我把最後測試的代碼先貼上來:
use filehandle::filehandler;use filehandle::catchWorkerId;print "\nTEST fildehander base class \n\n";my $filehandler = filehandler->new("c:\\test2.ini");print $filehandler->getDealedStrings();print "\nTEST fildehander sub-class \n\n";$filehandler = catchWorkerId->new("c:\\test2.ini");print $filehandler->getDealedStrings();$filehandler->exportToFile("C:\\test2_new.ini");
filehandler.PM和catchworkerId.PM是我們重要的兩個Module(class)。fileHandler類是父類並完成了一個檔案解析的基本動作:
開啟檔案
每行讀取
解析字串並儲存在類的陣列變數裡面
匯出到合適的檔案中
這樣我們就可以只在子類裡面完成需要變動的部分。請看子類代碼catchworkId.PM
package catchWorkerId;@ISA = qw(filehandler);use strict;use warnings;sub convert{ my ($self,$linetext) = @_; my $pattern = "[a-z0-9]{9}[(]"; my @a; if(@a=($linetext =~/$pattern/gio)) { $a[0]=~s/[(]//g; return (1,$a[0]); }return (0,"");}1;
代碼說明
子類代碼只是完成一個方法convert,它的輸入是一個$linetext 字串標量,我們進行Regex的判斷,如果滿足匹配則返回true+替換後的$linetext,如果不滿足就返回false+Null 字元串。
如果在C#這類對象強型別語言中我們可以這樣定義:
bool convert(string linetext, out string convetedlinetext){ //implemenet code}
由於Perl是一種解釋性非類型語言,因此我們需要變通的實現這一功能。 所以,如何在新的概念情況下,用舊有語言來實現特定的功能,就需要開發人員的創新能力,這些東西不會在教程上講述,而需要開發人員自己的摸索。
你看通過類的繼承的方式,我們在子類只定義函數就完成我們預設目標。需要注意這一句:
@ISA = qw(filehandler);
它的意思是,引入filehandler.PM的函數,首先在catchWorkerId中尋找調用函數,如果沒有找到,會繼續在filehandler.pm中尋找。通過這種方式,我們完成了變相實現了類的繼承。
這裡給出父類的實現:filehandler.PM
package filehandler;use strict;use warnings;#newsub new{ my($class, $filename) = @_; my $self ={ _filename =>$filename }; my @ss; $self->{_dealedStringArray}= \@ss; return bless($self,$class);}#really open filesub getDealedStrings{ my($self) = @_; open (FILEHANDLE,$self->{_filename})|| die("can't open file: $self->{_filename}"); #start to deal string line by line!while(<FILEHANDLE>){ $self->handleLine($_);}close (FILEHANDLE);return @{$self->{_dealedStringArray}} ;}#deal string line by linesub handleLine{ my ($self,$linetext) = @_; my @result = $self->convert($linetext); if($result[0]==1) { $self->_addstring($result[1]); }}#You can write a override function in sub classsub convert{ my ($self,$old) = @_; $old = "CONVERTED STRING:" .$old; return (1,$old);}#add the dealed string into a array.sub _addstring{ my ($self,$linetext) = @_; my @a =@{ $self->{_dealedStringArray}}; push(@a, $linetext."\n"); @{$self->{_dealedStringArray}} = @a;}#output converted filesub exportToFile{ my($self,$newFileName) = @_; open FILEHANDLE ,">$newFileName" || die("can't create file : $newFileName"); for(my $i=0;$i<@{$self->{_dealedStringArray}};$i++) { print FILEHANDLE "${$self->{_dealedStringArray}}[$i]"; } close FILEHANDLE;}1;
這裡不在具體講Perl 對象編程的概念,大家可以變參考背景知識裡面推薦的O'Reilly - Advanced Perl Programming,邊看本執行個體,一定會有所心得。