學習perl(6)

湖湘文化發表於2013-11-19
 

學習Perl5

繼續我的Perl之旅,哈哈

3. 以正規表示式處理文字

1)以s///進行替換

如果把m//模式匹配運算子想成文書處理器的“查詢”功能,那麼“查詢與替換”的功能就是Perls///替換運算子:

#!/usr/bin/perl -w

$_ = "He's out bowling with Barney tonight.";

s/Barney/Fred/; #Fred替換Barney

print "$_n";

下面的替換用到了第一個記憶體變數,也就是$1,進行模式匹配時它會被賦值:

#!/usr/bin/perl -w

$_ = "He's out bowling with Barney tonight.";

s/with (w+)/against $1/;

print "$_n";

s///會返回有用的布林值。它在替換成功時為“真”,否則為“假”:

#!/usr/bin/perl -w

$_ = "fred flintstone";

if (s/fred/wilma/) {

print "Successfully replaced fred with wilma!n";

}

注意,即使有其他可以替換的部分,s///也只會替換一次。

2)以/g進行全域性替換:

/g修飾符可讓s///進行所有可能的、互不重疊的替換:

#!/usr/bin/perl -w

$_ = "home,sweet home!";

s/home/cave/g;

print "$_n";

一個相當常見的全域性替換是縮減空白,也就是將任何連續的空白轉換成單一空格:

#!/usr/bin/perl -w

$_ = "Input datat may have extra whitespace.";

s/s+/ /g;

print "$_n";

刪除開頭和結尾處的空白:s/^s+//; s/s+$//;

一個步驟的寫法則是使用擇一匹配的豎線符號並配合/g修飾符:

s/^s+|s+$//g; #去掉頭尾的空白

3)不同的界定符號

對於一般沒有左右之分的(非成對)字元,用法跟使用斜線一樣,只使用3個界定符號即可:

S#^

但如果使用有左右之分的成對字元,就必須使用兩對:一對包住模式,一對包住替換字串:

s{fred}{barney}; s[fred](barney); s#barney#;

4)選項修飾符和繫結運算子

s#wilma#Wilma#gi;        s{_END_.*}{}s; #刪除結尾標記及之後的每一行

$file_name =~ s#^.*/##s; #移除$file_name裡所有Unix樣式的路徑

5)大小寫轉換

U跳脫字元會將其後的所有字元轉換成大寫:

#!/usr/bin/perl -w

$_ = "I saw Barney with Fred.";

s/(fred|barney)/U$1/gi;

print "$_n";

L跳脫字元會將其後的所有字元轉換成小寫;

#!/usr/bin/perl -w

$_ = "I saw Barney with Fred.";

s/(fred|barney)/L$1/gi;

print "$_n";

也可以使用E關閉大小寫轉換的功能;

s/(w+) with (w+)/u$2E with $1/i;

使用小寫形式(lu)時,它們只會影響之後的第一個字元:

s/(fred|barney)/u$1/ig;

甚至可以將它們並用,如使用uL來表示“全部轉小寫,但首字母大寫”:

s/(fred|barney)/uL$1/ig;

6)split運算子

split運算子會根據分隔符拆開一個字串,這對處理以製表符、冒號、空白或任意符號所分隔的資料相當有用。它的用法如下所示:@fields = split /separator/, $string;

split運算子將會以分隔符的模式掃過所指定的字串($string),並且返回由該模式所分隔出來的一串欄位(子字串)。下面以冒號做分隔符,是個典型的split模式:

@fields = split /:/, “abc:def:g:h”; #產生(”abc”,”def”,”g”,”h”

如果兩個分隔符放在一起,就會產生空的欄位:

@fields = split /:/, “abc:def::g:h”; #產生(”abc”,”def”,””,”g”,”h”

注意:split會保留開頭處的空欄位,卻會捨棄結尾處的空欄位。

利用/s+/模式以空白進行分隔也是常見的做法。在此模式下,所有的空白會被當成一個空格來處理:my $some_input = “This is a t test.n”;

Split預設會以空白字元拆開$_:(用一個空格來取代模式是split的特別用法)

my @fields = split; #等於 split /s+/,$_;

7)join函式

Join函式所達成的效果正好與split相反,它的用法如下所示:

my $result = join $glue,@pieces;

join會把膠合字串放進每個片段之間並且返回所得到的字串:

my $x = join “:”, 4,6,8,10,12; # $x”4:6:8:10:12”

使用上面的$x,我們可以先分解字串,再用不同的界定符號將它接起來:

my @value = split /:/,$x; my $z = join “-“,@value;

注意:雖然splitjoin合作無間,但是join的第一個引數是字串,而不是模式!

8)列表上下文中的m//

在列表上下文中使用匹配運算子(m//)時,如果模式匹配成功,則其所返回的列表內容是所有記憶體變數的內容;如果匹配失敗,則返回空列表:

#!/usr/bin/perl -w

$_ = "Hello there, neighbor!";

my($first, $second, $third) = /(S+) (S+), (S+)/;

print "$second is my $thirdn";

9)功能更強大的正規表示式

非貪心的量詞

貪心的量詞,/fred.+barney/  “回溯”

對於每一個貪心的量詞,都會存在一個非貪心的量詞。以加號(+)來說,我們可以改用非貪心的量詞+?,這表示至少有一次(跟加號一樣),但是所匹配到的字串卻是越短越好,而不再是越長越好。  /fred.+?barney/

如果要處理的字串是這樣;

I’am talking about the cartoon with Fred and Wilma!

應該使用非貪心的量詞,s#(.*?)#$1#g;

跨行的模式匹配

下面的寫法可以表示一個4行文字:

$_ = “I’m much betternthan Barney isnat bowling,nWilma.n”;

每一行的開頭和結尾:print “Found ‘wilma’ at start of linen” if /^wilman/im;

下面的程式會先把整個檔案讀進一個變數,然後把其他的檔名前置在每一行的開頭:

open FILE,$filename

or die “Can’t open ‘$filename’: $!”;

my $lines = join ‘’,;

$lines =~ s/^/$filename: /gm;

一次更新多個檔案

要在Perl中直接修改檔案內容,可利用鑽石運算子(<>)。

修改幾百個格式類似的檔案,只做三項改動:

#!/usr/bin/perl -w

use strict;

chomp(my $date = `date`);

$^I = “.bak”;

while (<>) {

s/^Author:.*/Author: Randal L. Schwwartz/;

s/^Phone:.*n//;

s/^Date:.*/Date: $date/;

print;

}

只從命令列來修改檔案

在命令列上使用單行程式修改

$ perl -p -i.bak -w -e ‘s/Randall/Randal/g’ fred*.dat

Perl的命令選項:

Perl的作用如同在檔案的開頭寫上#!/usr/bin/perl,表示以perl程式來處理隨後的事項;

-p選項用來要求Perl幫你寫程式,如同while (<>) { print;}

-i.bak選項,其作用是在程式開始執行之前把$^I設為”.bak”,做個備份;

-w選項,開啟警告功能;

-e選項用來告訴Perl,“程式程式碼來了”;

最後一個引數是fred*.dat,表示@ARGV的值應該是匹配此檔名模式的所有檔名;

把以上所有片段都組合在一起,就好像寫了下面這個程式以及把fred*.dat引數傳給它一樣:

#!/usr/bin/perl –w

$^I = ”.bak”;

while (<>) {

s/Randall/Randal/g;

print;

}

4. 其他控制結構

1)Unless控制結構和until控制結構

使用unless表示除非這個條件式為“真”,否則執行這對程式程式碼,它和if測試一樣,只是條件相反;如果你想讓程式塊在條件式為“假”時執行,請將if換成unless

你甚至可以在unless之後加上一個else子句;

Unless可以用if來改寫。

Until迴圈結構會一直執行,直到條件式為“真”,是改裝過的while迴圈。

2)表示式修飾符

為了簡化表達的方式,表示式後面可以接著一個用來控制它的修飾符:

print “$n is a negative number.n” if $n < 0;

注意到,即使表示式寫在後面,它仍然會先執行。

3)未修飾塊的控制結構

所謂的“未修飾”塊,就是沒有關鍵字或條件式的塊。它的塊內容只會被執行一次,是一個不迴圈的迴圈!
為臨時區域性變數提供一個有效的範圍,是它其中的一個特色:

#!/usr/bin/perl -w

{

print "Please enter a number:";

chomp(my $n = );

my $root = sqrt $n;

print "The square root of $n is $root.n";

}

4)自動遞增與自動遞減

快速而簡單的方法,用來檢查列表中有哪些專案並且計算每個專案出現了幾次:

my @people = qw{ fred barney wilma dino barney fred pebbles };

my %count ; #新的空雜湊

$count{$_}++ foreach @people; #必要時建立新的鍵與值

5)for控制結構

For迴圈目前最常見的用途,就是控制重複的運算過程:

#!/usr/bin/perl -w

for ($i = 1;$i <=10; $i++) {

print "I can count to $i!n";

}

#!/usr/bin/perl -w

for ($_ = "bedrock";s/(.)//; ) {

print "One character is $1n";

}

Foreachfor之間的秘密關聯

事實上,在Perl的解析器裡這兩個關鍵字是等價的。

如果圓括號裡有兩個分號,它會被當成for迴圈;否則,它其實是foreach迴圈:

for (110) { #事實上,這是從1到10foreach迴圈

 print “I can count to $_!n”;

}

Perl裡,真正的foreach迴圈幾乎總是比較好的選擇。

6)迴圈控制

last運算子會立即迴圈的終止(類似C語言的break),它是迴圈塊的緊急出口。Last運算子會對整個迴圈其作用;last運算子會對當前執行中的最內層的迴圈塊發揮作用。

next運算子會跳到當前迴圈塊的底端,在next之後,程式會繼續執行迴圈的下次迭代(類似C語言的continue;next只對最內層迴圈起作用。

redo運算子會跳到當前迴圈塊的頂端,而不經過任何測試條件,也不會前進到迴圈的下一次迭代;只對最內層迴圈起作用。

示範程式,體驗這3個運算子的運作方式:

#!/usr/bin/perl -w

foreach (1..10) {

print "Iteration number $_.nn";

print "Please choose:last,next,redo,or none of above?";

chomp(my $choice = );

print "n";

last if $choice =~ /last/i;

next if $choice =~ /next/i;

redo if $choice =~ /redo/i;

print "That wasn't any of the choices ... onward!nn";

}

print "That's all,folks!n";

習題:

寫一支程式,讓使用者不斷猜測範圍從1到100的秘密數字,直到猜中為止。程式以魔術公式int(1 + rand 100)來隨機產生秘密數字……

#!/usr/bin/perl -w

my $secret = int(1 + rand 100);

#print "Don't tell anyone,but the secret number is $secret.n";

while (1) {

print "Please enter a guess from 1 to 100:";

chomp(my $guess = );

if ($guess =~ /quit|exit|^s*$/i) {

print "Sorry you give up.The number was $secret.n";

last;

} elsif ($guess < $secret) {

print "Too small.Try again!n";

} elsif ($guess == $secret) {

print "That was it.Congratulations!n";

} elsif ($guess > $secret) {

print "Too large.Try again!n";

}

}

5. 檔案測試

1)檔案測試運算子

啟動會建立新檔案的程式之前,先檢查所指定的檔案是否真的存在,以免誤操作給覆蓋掉了,可以用-e檔案測試運算子來測試檔案是否存在:

die “Opps! A file called ‘$filename’ already exists.n”

if -e $filename;

檔案測試符組成自連字元和某個字母(代表要進行何種測試),後面接著所要測試的檔名或檔案控制程式碼。-r -w -x -o -e -z -s -f -d -l -t -T -B -M -A -C

2)關於stat函式和lstat函式

想知道檔案所有其他的相關資訊,可以使用stat函式,這個函式會返回相當豐富的資訊。函式stat的引數可以是檔案控制程式碼或是某個會返回檔名稱的表示式。它所返回的值可能是空列表,表示函式stat執行失敗;也可能是個含有13個元素的數字列表: $dev$ino$mode$nlink$uid$gid$size$atime$mtime$ctime$blksize$blocks

若你需要符號連結本身的資訊,可以使用lstat函式來代替stat

3)localtime函式

Perl可以在標量上下文中使用localtime函式來完成轉換:

my $timestamp = 1180630098; my $date = localtime $timestamp;

gmtime函式返回的是世界標準時間(格林尼治標準時間);如果需要從系統時鐘取得當前的時間戳,請使用time函式。  my $now = gmtime;

4)使用特殊的“下劃線檔案控制程式碼”

對特殊的_檔案控制程式碼進行statlstat或檔案測試(也就是僅以單一的下劃線為運算子),就是在告訴Perl從記憶體裡找出前一次檔案測試、statlstat的參考資料來用,而不是到外面向作業系統再要一次資料。

習題:

1):寫一支程式,從命令列取得一串檔名稱,並彙報這些檔案是否可讀、可寫、可執行以及是否存在:

#!/usr/bin/perl -w

foreach my $file (@ARGV) {

my $attribs = &attributes($file);

print "'$file' $attribs.n";

}

sub attributes {

my $file = shift @_;

return "does not exist" unless -e $file;

my @attrib;

push @attrib, "readable" if -r $file;

push @attrib, "writable" if -w $file;

push @attrib, "executable" if -x $file;

return " exests "unless @attrib;

'is' . join " and ",@attrib;

}

2)寫一支程式,從命令列引數所指定的檔案中找出最舊的檔案並且以天數彙報它已經存在多久了:

#!/usr/bin/perl -w

die "No file names supplied!n" unless @ARGV;

my $oldest_name = shift @ARGV;

my $oldest_age = -M $oldest_name;

foreach (@ARGV) {

my $age = -M;

($oldest_name,$oldest_age) = ($_,$age)

if $age > $oldest_age;

}

printf "The oldest file was %s,and it was %.1f days old.n",$oldest_name, $oldest_age;

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/21256317/viewspace-776995/,如需轉載,請註明出處,否則將追究法律責任。