正規表示式的入門與應用(三)
張耀仁
changyj@rtfiber.com.tw
http://www.rtfiber.com.tw/~changyj
目前任職於瑞泰纖維
作者同時譯有 Linux 核心研究篇
著作權所有﹐欲轉載請與筆者聯絡
本期介紹給讀者的內容有:
- 延伸型 RE﹐包含
- 在 Perl 中使用 RE﹐包含
- 引號及引號型運算子
- m// 運算子與 =~ 的結合
- 特殊序列
- 引用合乎 RE 的字串之某些部分
- m// 的傳回值因執行環境而異
- m//g 的運用
- s/// 運算子
延伸型 RE
延伸型(extended)RE 提供了比基本型 RE 更多的特殊字元﹐如 +, ?, 以及 |:
- X+:
- 代表由 X, XX, XXX, ... 等這些由連續 N 個(N >=1)X 所構成
的 RE 的集合。當字串符合 X, XX, XXX,... 中其中任何一個 RE 時﹐
就算符合 X+。
- 例如符合 ab+c 這個 RE 的字串有 abc, abbc, abbbc, ... 等﹐也就是字元 a
之後接著至少一個的 b﹐再接著 c;字串 ac 則不符合。
- 事實上﹐X+ 與 XX* 具有相同的意義(equivalent):
剛才提到的 ab+c 與 abb*c 是相等的;
我們也可以把 [0-9][0-9]* 寫成 [0-9]+。
- X?:
- 代表由空字串以及 X 這兩個 RE 所構成的集合。
- 空字串的長度為零﹐不含任何字元。
- 觀察 ab?c 這個 RE﹐由於符合 b? 的字串為空字串或 b﹐
因此符合 ab?c 的字串只有 ac 與 abc﹐而 abbc, abbbc, ... 等則不符合。
[問題12]
請利用 egrep 把含有 long 或 loong 的資料行列出來。
[說明]
- egrep 與 grep 的差別在於解譯 RE 的方式:前者採用延伸型﹐
而後者採用基本型 RE 的語法。
- 比較 long 與 loong 時發現﹐這兩個字串只相差一個 'o'﹐
可以把 RE 寫成 loo?ng﹐第二個 o 之後的 ? 代表這個 o 可有可無。
- 執行範例如下:
[changyj:~] cat data7
Edward Furloong
has
long hair.
[changyj:~] egrep -n 'loo?ng' data7
1:Edward Furloong
3:long hair.
特殊字元『|』
當利用『|』把幾個 RE 連接起來﹐例如
RE1 | RE2 | ... | REn 時﹐
代表字串只要符合 RE1, RE2, ..., REn
中的任何一個 RE 時即可﹐讀者可以把上式解讀成 RE1
『或』RE2『或』... REn。
『|』常被稱為 alternation operator。
以下利用檔案 data8 來示範『|』的用法。首先看看檔案 data8 的內容:
[changyj:~] cat data8
I keep a cat and
a dog. The cat
likes to eat fish
and the dog prefers
roasted beef.
想列出含有 dog『或』fish 字串的資料行時﹐可以把 RE 設計成『dog|fish』:
[changyj:~] egrep -n 'dog|fish' data8
2:a dog. The cat
3:likes to eat fish
4:and the dog prefers
如果要列出含有 cat『或』beef 字串的資料行﹐使用『cat|beef』這個 RE 即可:
[changyj:~] egrep -n 'cat|beef' data8
1:I keep a cat and
2:a dog. The cat
5:roasted beef.
而使用『cat|dog』這個 RE 則可列出含有 cat『或』dog 的資料行:
[changyj:~] egrep -n 'cat|dog' data8
1:I keep a cat and
2:a dog. The cat
4:and the dog prefers
我們發現檔案 data8 的第二行同時含有 cat 與 dog。那麼 egrep
究竟是因為發現該行有 cat﹐還是該行有 dog 才列出該行?
以下列出 egrep 處理該行的過程:
- 一開始的 A 點(請參考第一期的說明)在第一個字元﹐也就是字元 'a' 的位置。
egrep 先檢查在該位置是否可能出現符合 RE﹐也就是『cat|dog』的字串。
而處理『|』時的順序是由左而右﹐因此對於同一個 A 點﹐
egrep 會先檢查該位置是否出現 cat;否則則檢查該處是否有 dog 字串。
在發現該位置並未出現符合任何子 RE 的字串後﹐egrep 把 A 點移到下一個字元。
- 此時 A 點在第二個字元﹐由於該位置並未出現 cat 或 dog﹐
egrep 把 A 點移到下一個字元。
- 當 A 點在第三個字元時﹐雖然該處並沒有 cat 字串﹐但卻出現了 dog 字串符合子
RE『dog』﹐於是 egrep 把該行列出。
- 因此 egrep 是先發現該行有 dog 而把該行列出。
『( )』的使用
在延伸型 RE 中﹐『( )』有兩種功能﹐統稱為 Grouping:
- 維持『子 RE』的完整性:
當要設計只讓 company 及 companies 能符合的 RE 時﹐
我們發現這兩個字串前半部都是 compan。請參閱表 1:
[表1]
|
| 第一部分
| 第二部分
|
|---|
| 合乎要求的字串
| compan
| y 或 ies
|
| 相對應的 RE
| compan
| y | ies
|
如果把兩個部分的 RE 直接連接起來﹐得到『company|ies』﹐
這個 RE 代表要搜尋的字串是 company 或是 ies﹐顯然與原先的構想不同。
解決的方法是藉由『( )』來維持『y|ies』的完整性﹐
把 RE 寫為『compan(y|ies)』﹐執行範例如下:
[changyj:~] cat data9
Although there are many companies
selling computers, our
company is the most famous one.
[changyj:~] egrep -n 'compan(y|ies)' data9
1:Although there are many companies
3:company is the most famous one.
- 使多字元的 RE 與 *, +, ? 等特殊字元結合:
在介紹基本型 RE 時﹐曾強調 * 必須與佔有一個字元位置的 RE(one-character RE)
結合;在延伸型 RE 中﹐只要把多字元的 RE 利用一組『( )』含括起來
即可與 *, +, ? 等特殊字元結合。例如要尋找 cabc, cababc, cabababc, ... 等
由字元 c 再接著若干組 ab 再接上 c 的字串時﹐可以把 RE 設計為『c(ab)+c』﹐
此時與特殊字元 + 結合的 RE 是 ab:
[changyj:~] echo 'ccababcc' | egrep -n 'c(ab)+c'
1:ccababcc
一般而言﹐grep, sed 等支援的是基本型 RE;而 egrep, perl 支援的是
延伸型 RE。表 2 列出了延伸型 RE 的特殊字元﹐以及在基本型 RE 中相對應的寫法;
若標示『無』則代表該類 RE 並未提供該項功能:
[表2]
| 延伸型 RE 的特殊字元
| 在基本型 RE 內的寫法
|
|---|
'.', ^, $,
[...], [^...]
| 同左
|
| *, +, ?, |, {min,max}
| *,無,無,無,\{min,max\}
|
| (...)
| \(...\)
|
| (...)*, (...)+, (...)?
| 無
|
| [註1]
| 讀者在 GNU 的 grep 與 sed 中可以使用延伸型 RE 內的 + 與 ?﹐
寫法分別是 \+ 與 \?。
| | [註2]
| GNU 的 grep 與 sed 允許 *, \+, \? 與多字元的 RE 結合﹐
只要把該 RE 放在一組 \( 及 \) 內即可。
|
|
如何在 Perl 中使用 RE
本節的目的在於介紹如何在 Perl 上使用 RE﹐
筆者假設讀者對 Perl 已有基本的認識﹐文中將不詳述與 RE 無關的部分。
引號與引號型的運算子
在 Perl 中﹐我們必須在字串型資料的前後各加上
一個單(雙)引號﹐例如 'It is fine...', "Are you OK?"﹐以便把不屬於
該字串的其他字元分隔開來。
Perl 對於單、雙引號有不同的處理方式:
- 單引號:
把字串內的『\\』視為『\』﹐『\'』視為『'』﹐其他則不受影響。
- 雙引號:
- 對 escape 序列進行處理:把 \t, \n, \r, \f 等分別視為
定位(TAB)字元、跳行(NL)字元、歸位(CR)以及跳頁(FF)字元等。
- 若 $ 及 @ 前方沒有與其結合的 \ 時﹐Perl 會把緊臨 $ 及 @
之後的文數字元視為變數及陣列名稱﹐並把『$變數名』及『@陣列名』
換成變數及陣列的內容。
請參考以下的範例:
$var = '\n'; # $var 的內容為兩個字元:'\' 以及 'n'
$var = "\n"; # $var 的內容為一個字元:跳行字元
$name = 'John';
$var = 'My name is $name..'; # $var 的內容為 My name is $name..
$var = "My name is $name.."; # $var 的內容為 My name is John..
相較於其他的程式語言﹐Perl 提供了『引號型運算子』﹐
讓使用者可以選擇除了 ' 與 " 之外﹐任何非文數字(non-alphanumeric)及
非空白字元(non-whitespace)的字元來做為分隔符號(delimiter):
- 單引號型運算子:
- 寫法為 q/字串/﹐與 '字串' 同義。例如 'It is fine...' 可寫成
q/It is fine.../(以 / 為分隔符號)﹐
也可寫成 q!It is fine...!(以 ! 為分隔符號)等。
- 但如果採用 ()[]{}<>其中的字元來做為分隔符號時﹐
則必須以 q(字串), q[字串], q{字串} 或 q<字串> 這種成對的格式來使用﹐例如:q(It is fine...)。
- 雙引號型運算子:寫法為 qq/字串/﹐與 "字串" 同義。
有關引號型運算子的詳細說明﹐
請參考 perlop manpage 內 'Quote and Quote-like Operators' 一節。
尋找符合 RE 的字串-- =~ 與 m/RE/ 的結合
要檢查一個變數是否含有符合 RE 的字串時﹐
我們可以利用運算子 m/RE/ 或 /RE/﹐並與 =~ 合用﹐格式為
變數 =~ m/RE/ 或是
變數 =~ /RE/
當變數含有符合 RE 的字串時﹐運算的結果為真﹐否則為偽。
[問題13]
請寫一 Perl 程式﹐當使用者輸入的資料含有數字時﹐印出『有數字』的訊息。
[說明]
程式如下:
#!/usr/bin/perl
while(1)
{ print "請輸入: ";
chop($input=<STDIN>);
if($input =~ m/[0-9]+/) { print "有數字...\n\n"; }
}
在這個程式中:
- 有一個無窮迴圈(while(1) {...})。
- 使用者每輸入一行﹐ 該行的內容會被存入 $input 這個變數中($input=)﹐
包含按 ENTER 時產生的跳行字元(newline﹐\n)。
- 我們利用 chop($input) 來刪除 $input 內的最後一個字元﹐也就是跳行字元。
- 當 $input 內含有數字時會使得 if(m/[0-9]+/) 成立而印出『有數字』的訊息。
例如輸入『number 235811 text 44759』時:
- m// 由變數內容前端開始搜尋。
- 首先發現 2 這個字元符合 [0-9]。
- 由於『+』是個貪心的字元﹐m// 會儘可能把從字元 2 開始的連續多個數字
- 字元納入 [0-9]+ 的範圍﹐因此 m// 找到第一個符合 /[0-9]+/ 的字串是
235811。
採用 m/RE/ 而不是 /RE/ 的好處在於前者可以使用
其他的字元來做為分隔符號﹐方式與先前介紹的『引號型運算子』相同。
[問題14]
請寫一 Perl 程式讓使用者輸入一路徑名稱﹐並判斷該路徑是否為絕對路徑。
[說明]
程式如下:
#!/usr/bin/perl
while(1)
{ print "請輸入路徑: ";
chop($path=<STDIN>);
if($path =~ m|^/|) { print " $path 是絕對路徑\n\n"; }
else { print " $path 是相對路徑\n\n"; }
}
在這個程式中﹐
- 我們以『 $path 的內容是否以 / 開頭』來做為是否為絕對路徑的依據。
- 其中 ^ 為定位字元﹐代表變數內容的最前端﹐
如果為 $ 則為變數內容的末端。
- 如果不改變分隔符號的寫法是 $path =~ m/^\//。
- 我們可以把 m|^/| 寫成 m(^/)、m[^/]、m{^/} 或 m<^/>。
- print 後的字串是以雙引號來做為分隔符號﹐而 '$path' 之前並沒有 \﹐
因此 Perl 會以 $path 的內容來取代 '$path' 這五個字元。
特殊序列
為簡化 RE 的寫法﹐Perl 定義了一些特殊序列(meta sequence)﹐請參考表 3:
[表三]
| 特殊序列
| 說明
|
|---|
| \w
| 等於 [A-Za-z0-9_]﹐由文數字元(alphanumeric)及
底線 _ 所構成的字元集合
|
| \W
| 等於 [^A-Za-z0-9_], 為 \W 的反相字元集合
|
| \s
| 由空白字元所構成的字元集合﹐通常為 [ \t\n\r\f]
|
| \S
| \s 的反相字元集合
|
| \d
| 等於 [0-9]﹐由數字字元(digit)所構成的字元集合
|
| \D
| 等於 [^0-9]﹐為 \d 的反相字元集合
|
因此﹐問題 13 中的 $input =~ m/[0-9]+/ 可改寫成 $input =~ m/\d+/﹐
也可寫成 $input =~ m{\d+} 等。
引用合乎 RE 的字串之某些部分
當變數內含有符合 RE 的字串時﹐Perl 允許我們引用該字串的某些部分﹐說明如下:
- 設計 RE 時在欲引用的部分前後各加上『(』與『)』做為標記﹐
在此『( )』的功能是『擷取文字』(capturing text)。
- RE 中的『(』與『)』必須成對(不考慮與『\』結合者)。
- 把 RE 中的『(』加以編號﹐編號從 1 開始﹐由左向右遞增。
- 對於經由『( )』標記的部分﹐如果標記該組的『(』之編號為 N﹐
則該組的編號亦為 N﹐我們利用『$N』來引用該組的內容。
- Perl 採用延伸型 RE﹐因此『( )』也能做為 grouping 之用
(請參考『( ) 的使用』一節)。
即使我們要某些『( )』擔任的是 grouping 的角色﹐Perl 仍賦予它擷取文字的
功能;因此在編號時仍須計算在內。
- 在 sed 中是利用『\(』與『\)』來標記﹐以『\編號』來取用﹐
與 Perl 不同﹐請讀者留意。
[問題15]
如果 $var 內含有符合像 '12:23:52' 這種格式的字串時﹐
請把該字串所代表的時、分、秒印出來﹐並分別存到 $hour, $min, $sec 內。
[說明]
- 首先設計出讓 '12:23:52' 這種格式的字串能符合的 RE:『\d+:\d+:\d+』。
在此不使用 \d\d 而採用 \d+ 是為讓時(分、秒)
只有一位數的情況﹐像 8:15:4﹐也能符合所設計的 RE。
- 接著分別把代表時、分、秒的部分標記起來﹐得到『(\d+):(\d+):(\d+)』。
時、分、秒分別是第一、二、三組用『(』及『)』標記起來的部分。
- 當有字串符合『(\d+):(\d+):(\d+)』這個 RE 時﹐
我們分別可用 $1, $2 及 $3 來引用字串中屬於時、分、秒的部分。
程式範例如下:
#!/usr/bin/perl
while(1)
{ print "請輸入: ";
chop($var=<STDIN>);
if($var =~ m/(\d+):(\d+):(\d+)/)
{ $hour = $1; $min = $2; $sec = $3;
print " 時=$1, 分=$2, 秒=$3\n";
print " \$hour=$hour, \$min=$min, \$sec=$sec\n\n";
}
}
- 如果想同時印出整個符合的字串時﹐可以把整個 RE 用一組『( )』
標記起來﹐成為『((\d)+:(\d+):(\d+))』。此時 $1 的值即為整個符合
的字串﹐而字串中屬於時、分、秒的部分則分別是 $2, $3 與 $4。程式改寫如下:
#!/usr/bin/perl
while(1)
{ print "請輸入: ";
chop($var=);
if($var =~ m/((\d+):(\d+):(\d+))/)
{ $hour = $2; $min = $3; $sec = $4;
print " Time=$1, 時=$2, 分=$3, 秒=$4\n";
print " \$hour=$hour, \$min=$min, \$sec=$sec\n\n";
}
}
- 由於 Time=13:246:7890 這種不合理的字串也符合剛才設計的 RE﹐
為使程式更加完善﹐我們可以增加做為判斷之用的程式碼而改寫成
if($var =~ m/(\d+):(\d+):(\d+)/)
{ if($1 >=24 || $2 >=60 || $3 >= 60)
{ print "時間格式不正確!!\n";
}
else
{ $hour = $1; $min = $2; $sec = $3;
print "\$hour=$hour, \$min=$min, \$sec=$sec\n";
}
}
即可﹐不必堅持要改寫 RE;實際上在設計 RE 時﹐
如果要把所有條件都考慮進去﹐往往是耗時且困難的。
m// 的傳回值因執行環境而異
在剛才的例子中﹐'if' 所能接受的值屬於純量型(scalar)﹐
因此 'if' 要求 $var =~ m// 傳回純量值(例如真、偽);
我們稱此時 $var =~ m// 是在純量環境(scalar context)下執行。
如果要求 $var =~ m// 傳回串列(例如利用它的傳回值來設定陣列等)﹐
則稱它是在串列環境(list context)下執行﹐分為兩種情形:
- RE 中有利用『(』及『)』標記起來的部分:
當變數含有符合 RE 的字串時﹐$var =~ m// 會傳回由 $1, $2, ... 等變數值
所構成的串列;否則傳回空的串列。
[問題16]
如果 $var 內含有符合像 '12:23:52' 這種格式的字串時﹐
請印出該字串所記錄的時、分、秒。
[說明]
程式範例如下:
#!/usr/bin/perl
@label=('時','分','秒');
while(1)
{ print "請輸入: ";
chop($var=<STDIN>);
$i=0;
foreach $x ($var =~ m/(\d+):(\d+):(\d+)/)
{ print " $label[$i]=$x\n";
$i++;
}
}
| (1)
| foreach $x 之後需要一個串列﹐因此 $var =~ m// 是在串列環境下執行。
|
| (2)
| 如果使用者輸入『I'll meet him at 11:34:5 in the park.』時﹐
由於 11:34:5 符合 RE﹐m// 將傳回由 ('11', '34', '5') 這樣的串列。
我們再透過 foreach 的迴圈把每一個元素列出。
|
| (3)
| 如果使用者輸入『I hate regular expression!』時﹐由於其中並無符合 RE
的字串﹐m// 將傳回空串列;foreach 則不會執行迴圈。
|
- RE 中沒有利用『(』及『)』標記起來的部分:
當變數內有字串符合 RE 時﹐傳回串列 (1);否則傳回空的串列。
m//g 的運用
當 m// 找到第一個符合 RE 的字串後就不會再繼續搜尋;如果要求 m// 找出所有符合
RE 的字串時則必須使用 g 選項。在不同的執行環境下﹐m//g 也有不同的回應:
- 串列環境:
- RE 中有標記起來的部分時﹐m//g 傳回的串列是由
每一個符合 RE 的字串之 $1, $2, $3,... 所構成。
把問題 16 的程式改寫如下:
#!/usr/bin/perl
@label=('時','分','秒');
while(1)
{ print "請輸入: ";
chop($var=<STDIN>);
$i=0;
foreach $x ($var =~ m/(\d+):(\d+):(\d+)/g)
{ print " $label[$i]=$x ";
$i++;
if($i == 3) { $i=0; print "\n"; }
}
}
此時如果輸入『meet him at 11:22:33, and her at 12:44:55』時﹐
foreach 所接收到的串列將是 ('11','22','33','12','44','55')。
- RE 中沒有標記起來的部分時﹐m//g 會傳回由所有符合 RE 的字串所構成的串列。
若把 $var=~ ... 改寫為『$var=~ m/\d+:\d+:\d+/g』且輸入與剛才相同的資料時﹐
foreach 接收到的串列將是 ('11:22:33', '12:44:55')﹐
與 m/(\d+:\d+:\d+)/g 有相同的效果。
- 純量環境:
m//g 會從上一個符合 RE 字串之後的第一個字元開始搜尋;
若是第一次搜尋或上次搜尋失敗時﹐則從變數內容的第一個字元開始。
當找到符合 RE 的字串時傳回值為真﹐否則為偽﹐
通常配合迴圈來使用。以下的程式將列出 $input 內所有的數字:
#!/usr/bin/perl
while(1)
{ print "請輸入: ";
chop($input=<STDIN>);
print "找到的數字有 ";
$i=0;
while($input =~ m/(\d+)/g)
{ $i++; print " $1 ";
}
print "\n一共有 $i 個數字..\n\n";
}
s/// 運算子
還記得在 sed 中我們利用編輯命令 s 來進行字串取代的動作嗎?
Perl 也提供了格式相同的 s 運算子﹐可用來修改變數的內容。範例如下:
#!/usr/bin/perl
$var = "The good boy has a good cat and a GooD dog.";
print " \$var 先前的內容是 $var\n";
$var =~ s/good/bad/;
print " \$var 現在的內容是 $var\n";
執行上述程式時發現在 $var 中只有『第一個』good 被換成 bad﹐
如果要把所有的 good 換成 bad﹐則必須加上 g(代表 global 之意)選項﹐
把敘述改寫成『$var =~ s/good/bad/g;』即可。
如果不區分大小寫﹐要把 good, Good, gOOD 等都換為 bad 時﹐則必須再加上
i(代表 ignore case 之意)選項﹐把原敘述改為『$var =~ s/good/bad/gi;』即可。
在 Perl 的 s/// 運算子中﹐不論是要引用符合 RE 的字串﹐或是字串的某些部分﹐
或是改變分隔符號﹐均採用與 m// 相同的方式。做個實驗:
#!/usr/bin/perl
$var = "abc123def";
print " \$var 先前的內容是 $var\n";
$var =~ s/(\d)/$1$1/g;
print " \$var 現在的內容是 $var\n";
$path = "/usr/bin/ghostview";
print " 原先 \$path 的內容是 $path\n";
$path =~ s|/usr/bin|/usr/local/bin|;
print " 現在 \$path 的內容是 $path\n";
這個程式的前半部會把變數 $var 內每一個(因為用了 g 選項)數字字元重覆一次﹐
因此 $var 的內容會變成 abc112233def。後半部則把 $path 內的 /usr/bin 換成
/usr/local/bin。
[問題17]
假設 $var 內容是由三個以『,』分隔開的欄位所構成﹐
現在我們想把第 1 與第 2 個欄位對調。
[說明]
程式如下:
#!/usr/bin/perl
$var = "欄位 1, 欄位 2, 欄位 3";
print " \$var 先前的內容是 $var\n";
$var =~ s/^([^,]*),([^,]*)/$2,$1/;
print " \$var 現在的內容是 $var\n";
由於是以逗號來區隔欄位﹐我們假設每個欄位內不會有逗點出現﹐
因此可以把每個欄位的內容想成是由
『零或多個非逗號的字元』 所構成﹐可以用『[^,]*』這個 RE 來描述。
Perl 的 s/// 提供了一個很實用的選項 e﹐以例子來說明:
#!/usr/bin/perl
$var = "num 54321 num 1357 num 246";
print " \$var 先前的內容是 $var\n";
$var =~ s/(\d+)/sprintf("%06d", $1)/ge;
print " \$var 現在的內容是 $var\n";
- s/// 的 RE 部分為 \d+﹐Perl 會嘗試以『貪心』(即越多越好)
的方式來尋找連續多個的數字字元。
- 在 $var 中﹐Perl 找到第一個符合 \d+ 的字串是 54321。
由於使用了 e 選項﹐Perl 會先把 sprint("%06d", $1) 當成一般的式子來執行﹐
並以該式子所產生的資料來做為『用來取代的字串』。
而 sprintf("%06d", $1) 會把 $1 的值以六位數的方式印出﹐
不足六位時會在前方以 0 補滿。
由於此時 $1 的值為 54321﹐sprintf() 的結果為 054321﹐因此 Perl
會把 $var 內的 54321 換成 054321。
- 第二、三個符合 \d+ 的字串分別是 1357 與 246﹐
而執行 sprintf() 後產生的字串分別是 001357 與 000246﹐
因此 Perl 分別用 001357 以及 000246 把兩者取代掉。
結語
筆者借用了三期的篇幅﹐向各位介紹 RE 的概念﹐並示範如何在
grep, sed, egrep 與 Perl 上使用 RE。
三期的內容﹐只能讓各位初窺 RE 的奧妙﹐無法讓各位馬上成為 RE 的行家﹐
畢竟要熟悉 RE﹐除了多加思考與練習﹐並沒有其他的捷徑。
冀望將來能有機會﹐與各位一起更深入地研究正規表示式。
參考書目
- Perl 的 manpages:
Perl manpage 可算是第一手的參考資料﹐撰寫得相當詳細。
本期主要參考的部分為 perlre 及 perlop;前者介紹 Perl 中的 RE 機制﹐
而後者著重於 perl 的運算子。由於本文所介紹只是 Perl RE 的一小部分﹐
建議有心鑽研的讀者印出這兩份 manpage﹐詳細閱讀。
- Mastering Regular Expressions﹐作者為 Jeffrey E. F. Friedl﹐
O'Reilly & Associates 出版﹐適合已熟悉 RE 的讀者進階之用。
- 第一章:簡介﹐以 egrep 來示範 RE 的用途。
- 第二章:簡介如何在 Perl 中使用 RE。
- 第三章:各種工具程式所提供的 RE 機制。
- 第四章:工具程式如何根據使用者指定的 RE 來進行搜尋。
- 第五章:如何建構一個有效率的好 RE。
- 第六章:有關 Awk, Tcl, GNU Emacs 在 RE 方面的資訊。
- 第七章:深入探討 Perl 提供的 RE 運算子。
很有份量及深度﹐不是一本容易讀的書﹐但讀者如果能好好讀過﹐
相信將獲益良多。