在04-基本比對中有提過了基本的比對,此篇更深入一點。
取回比對的內容
如果想取得比對的結果,使用小括號 '()',取回符合的內容用 $1, $2, $3, ...:
舉例而言,如果我們想要取回 email 中的@前面的username和@後面的domain name:
$email = "server\@n.sfs.tw"; $email =~ /([^@]+)\@(.+)/; print $1; # server print $2; # n.sfs.tw print $&; # server@n.sfs.tw
$email 是被比對的變數,注意這裡的@前面有一個脫逸字元'\',是因為@在雙引號字串中會被當成陣列,所以一定要加上,如果是單引號字串 就不必加。
=~ 符合比對運算子
/([^@]+)\@(.+)/ 比對的樣式說明如下
() 小括號中的內容是要取出符合的內容
[] 中括號是符合其中字元的集合
^@ 在中括號一開始加上這個符號代表是「否定」的意思,全部的意思是只要不是 '@'都行
+ 代表出現1個以上
\@ 因為在樣式裡面也是能放變數,所以前面加上脫逸字元'\'來確保要比對的是符號'@',如果沒加'\',PERL會判斷這個陣列變數是否存在,有存在就會置換成該變數;不存在時就當成是一般字元處理,有時也許會造成錯誤。
() 小括號中的內容是要取出第二個符合的內容
. 代表任一個字元
+ 代表出現1個以上
因此如果$email符合比對的樣式,第3行和第4行用特殊變數 $+數字依序取回符合的內容。
$& 代表比對成功的部分,這是PERL的特殊變數。
另外,
$` 代表比對到的字串之前半段所有字串
$' 代表比對到的字串之後半段所有字串
例如我們取出IP中每個字節的數字
$ip='211.75.194.243:8080'; $ip=~ /((\d+)\.(\d+)\.(\d+)\.(\d+)):(\d+)/; print "$1, $2, $3, $4, $5, $6\n"; # 211.75.194.243, 211, 75, 194, 243, 8080
可以看到 $1是第一個左括號的範圍。
上面的程式要做一點調整,因為有時比對不成功的話 $1 = $2 = $3 = ... = '' 會是空字串,必須加上判斷
$ip='AAA.75.194.243:8080'; if($ip=~ /((\d+)\.(\d+)\.(\d+)\.(\d+)):(\d+)/){ print "$1, $2, $3, $4, $5, $6\n"; }else{ print "比對失敗\n"; }
這樣就能做錯誤控制。
取回比對的內容同時也加入比對
有時我們需要把比對成功的字串同時再比對,例如要找出有連續字元的單字,例如 look, snoop, peer, employee 符合條件。
這時就需要用到特殊變數 \1, \2, \3, ...,和$1, $2, $3不同的地方在於它們只能放在樣式之中,來看看範例。
@words= qw ( usually positive floor flee eels country ); foreach(@words){ if( $_ =~ /([a-z])\1/ ){ print $_. "\n"; } }
第4行 利用\1將第一個比對符合的小括號再次比對,來檢查重覆出現的字元。
結果
usually
floor
flee
eels
整篇文章中的文字搜索
上面學到了怎麼取出具有重覆字元的單字,如果是整篇文章要取資料。來看範例:
$doc=<<"DOC"; If FIRST or INCREMENT is omitted, it defaults to 1. That is, an omitted INCREMENT defaults to 1 even when LAST is smaller than FIRST. The sequence of numbers ends when the sum of the current number and INCREMENT would become greater than LAST. FIRST, INCREMENT, and LAST are interpreted as floating point values. INCREMENT is usually positive if FIRST is smaller than LAST, and INCREMENT is usually negative if FIRST is greater than LAST. FORMAT must be suitable for printing one argument of type 'dou‐ ble'; it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point decimal numbers with maximum precision PREC, and to %g otherwise. GNU coreutils online help: <http://www.gnu.org/software/coreutils/> Report seq translation bugs to <http://translationpro‐ ject.org/team/> DOC while($doc =~ /[\w]*([\w])\1[\w]*/g){ print $&. "\n"; }
第1行 $doc只是一篇文章
第13行 樣式是 /[\w]*([\w])\1[\w]*/g 說明如下
[\w]* \w代表[a-zA-Z_0-9]這些字元,有出現0個字元以上,當出現0個時,重覆字元會在單字前面出現。
() 小括號中的內容是要取出符合的內容
[\w]+ 小號內代表有1個字元以上
\1 當第1個小括號比對符合時,再拿來比對,表示連續出現的字元
[\w]*代表\w出現0個字元以上,當出現0個時,重覆字元會在單字後面出現。
第14行是印出全部比對的範圍。
在樣式的最後面有一個字元 'g' ,這並不是打錯,而是稱之為「修飾子」的字元,這算是一個比對位置指標,下次的比對會從上一次比對符合的目標下一個字元找起。千萬不能省略這個g,省略的話會掉入無窮迴圈中。有關於更多修飾子的內容會在下一篇說明。
結果
omitted
omitted
smaller
current
usually
smaller
usually
all
http
www
http
有人會問,如果把第13行改成
while($doc =~ /([\w])\1/g){
不就剛好能找出兩個連續的字元了嗎,為什麼還有多旁邊的 [\w]* ,理由很簡單,因為那樣的輸出結果會是:
tt
tt
ll
rr
ll
ll
ll
ll
tt
ww
tt
你不會知道那是什麼字。
上一篇 13- 變數的視界
回到目錄 01-撰寫第一隻PERL程式
下一篇 15-進階比對 #2--使用更多修飾子