程式扎記: [Perl 學習手冊] CH07 : 正規表示式的世界

標籤

2010年8月25日 星期三

[Perl 學習手冊] CH07 : 正規表示式的世界


前言 :
Perl 有需多異於其他語言的特色. 在這些特色中最重要的一項就是對正則表示式的強力支援. 這些支援提供了快速, 彈性與可靠的字串處理能力.

何謂正則表示式 :
正則表示式 (regular expression) 在 Perl 裡通常稱為樣式 (Pattern), 是一個 "符合" 或 "不符合" 特定字串的範本. 也就是說, 有無限可能的文字字串, 而任何一個樣式都可以將這個無限集合分成兩個群組 : 符合此樣式與不符合的. (Wiki : 正則表示式)

樣式的簡易用法 :
要以某個樣式 (正則表示式) 來比對 $_ 的內容, 請將它放在兩個斜線 (/) 之間, 如下所示 :
  1. $_ = "yabba dabba doo";  
  2. if(/abba/) {  
  3.     print "Match!!\n";  
  4. }  

/abba/ 運算式會在 $_ 中尋找由 "abba" 這四個字符所組成的字串 ; 如果找到了, 它就會傳回真. 上面範例它不止找到一個字串, 但是這並不會影響結果. 只要找到了就算是比對成功 ; 如果沒有就算是比對失敗. 所有能在雙引號內字串使用的 "倒斜線規避序列", 也都可以在樣式裡使用.

- 關於中介字符
當然如果樣式只能比對簡單的文字字串, 那還不算是特別有用. 所以還有許多稱為中介字符 (metacharacter) 的特殊符號, 它們在正則表示式中具有特別的意義.
舉例來說 , 點號 (.) 是個萬用字符或稱通配符. 它會符合換列字符 (\n) 以外的所有單一字符. 因此 betty 將會符合 /bet.y/ 這個樣式. 注意的是點號只用來比對一個字符. 所以如果你想要比對字串中的句號 (period) , 雖然也可以用點號, 但這樣也會符合其他不相干的字符. 要是希望點號只能符合句號本身, 在前面加上倒斜線就行了. 此規則亦適用於任何其他 Perl 正則表示式裡所用到的中介字符 ; 在中介字符前面加上倒斜線, 就會使它失去中介字符的特殊作用.
因此倒斜線也是其中一個中介字符. 如果要得到真正的倒斜線, 請用兩個倒斜線表示.

- 簡單的量詞
在一個樣式中重復某些字眼, 是很常見的情況. 星號 (*) 會比對它前一個字符/項目零次或更多次. 因此 /fred\t*barney/ 這個樣式將會符合任何在 fred 與 barney 之間含有任意數目的跳格字符. 因為星號表示 "零或更多", 所以兩個字符之間可以有數百或完全沒有跳格字符. 但是除跳格字符就不會出現其它字符了.
如果除了跳格字符外, 還想比對其它字符該怎麼辦? 點號會比對任何字符. 因此 ".*" 會比對任何字符無限次. 也就是不管 fred 與 barney 之間夾著 "隨便甚麼東西" 都會符合樣式 /fred.*barney/. 因此我們稱 ".*" 為 "隨便什麼東西" 樣式.
星號的正式名稱是量詞 (quantifier), 意思是說它只訂了前一個項目的數量. 但是它不是唯一的量詞 ; 加號 (+) 是另外一個. 加號會比對前一個項目一次以上, 例如 /fred +barney/ 會符合在 fred 與 barney 之間用空格隔開, 而且只用空格隔開的字串. (空格不是中介字符) 它不會符合 fredbarney, 因為加號表示成在兩個名稱間必須要有一個以上的空格.
第三個量詞和星號與加號類似, 但是它的限制更為嚴格. 這個量詞是問號 (?), 表示前一個項目是可有可無. 也就是說前一個項目可以出現一次或是不出現. 因為這三個量詞指定了前一個項目重複的次數, 所以他們都必須接在某個東西之後.

- 樣式分組
在數學中, 圓括號 (()) 具有分組的作用. 在樣式比對中, 它也是中介字符. 舉例來說 樣式 /fred+/ 會符合像 fredddddd 這樣的字符, 可是這種字串實際上不常出現. 不過樣式 /(fred)*/ 會符合像 fredfredfred 這樣的字串, 這樣比較可能是你想要的.
圓括號讓我們得以直接在樣式比對中重複使用部分的字串. 透過回溯參照 (back reference) 我們可以引用圓括號中所找到的內容. 回溯參照會被表示成一個倒斜線後面跟著一個數字, 例如 \1, \2 等等. 數字用於表示特定的圓括號分組. 以圓括號括住點號, 可用於比對任何非換列字符. 回溯參照 \1 可用於將圓括號中所找到的內容再拿來處理 :
  1. $_ = "yabba dabba doo";  
  2. if(/(.)\1/) { #Find bb  
  3.     print "Get two same chars stick to each other!!!\n"  
  4. }  
樣式 (.)\1 讓我們找到兩個相同且緊接在一起的字符. 這裡會找到 bb.
回溯參照不必緊接在圓括號分組之後. 下面樣式比對用於比對 "y 之後跟著四個非換列字符", 並且以回溯參照 \1 指出還要比對 "d 之後跟著相應圓括號分組所找到的內容" :
  1. $_ = "yabba dabba doo";  
  2. if(/y(....) d\1/) { # match yabba dabba  
  3.     print "Get it!!\n"  
  4. }  
我們可以同時使用多個圓括號分組, 每個分組有自己的回溯參照. 參考範例如下 :
$_ = "yabba dabba doo";
  1. if(/y(.)(.)\2\1/) { # match 'abba'  
  2.     print "Get it!!\n"  
  3. }  
但問題來了, 我們怎麼知道哪個數字對應到哪個分組? 還好 Larry 做法十分簡單 : 計算左圓括號的個數並忽略套疊的情況 :
$_ = "yabba dabba doo";
  1. if(/y((.)(.)\3\2) d\1/) { # match 'abba'  
  2.     print "Get it!!\n"  
  3. }  
也許將正規表示式寫成下面形式會比較容易計算 :
  1. (  # 第一個左圓括號  
  2.     (.) # 第二個左圓括號  
  3.     (.) # 第三個左圓括號  
  4.     \3  
  5.     \2  
  6. )  
Perl 5.10 為回溯參照提供一個新的表示法. 此表示法使用的不是倒斜線與一個數字, 而是使用 \g{N}, 其中 N 是你想要使用之回溯參照的編號. 在樣式中使用此表示法將可以避免一些誤判的情形. 所以我們可以將回溯參照與樣式的字面部分區開來 :
  1. $_ = "aa11bb";  
  2. if(/(.)\g{1}11/) { # match 'aa'  
  3.     print "Get it!!\n"  
  4. }  
使用 \g{N} 表示法時, 我們還可以使用負數. 除了以絕對數字編號來指定圓括號分組 (一般回溯參照), 我們還可以使用相對數字編號來指定圓括號分組 (相對回溯參照). 例如我們可以使用 -1 將前面的例子改寫成 :
  1. $_ = "aa11bb";  
  2. if(/(.)\g{-1}11/) { # match 'aa'  
  3.     print "Get it!!\n"  
  4. }  
- 擇一比對
豎線 (|) 在這裡, 使用上通常念為 "或" (or), 表示左邊或右邊符合. 也就是說如果豎線左半邊的樣式比對失敗, 豎線右邊有可能會比對成功. 因此 /fred|barney|betty/ 對任何提到 fred 或 barney 與 betty 的字串都進行比對. 現在我們可以使用 /fred( |\t)+barney/ 這般樣式, 來比對 fred 和 barney 之間是否存在空格, 跳格或兩者的組合. 加號表示重複一次或更多次 ; 每次只要有重複, ( |\t) 都會對空格與跳格進行比對. 若你希望 fred 與 barney 之間的字符全都一樣, 你可以將上述樣式改成這樣 : /fred( +|\t+)barney/ .如此一來中間的分隔符一定全是空格或全是跳格.

字符集 :
字符集 (character class) 就是方括號 ([]) 一連串可能的字符. 它只會符合單一字符, 但該字符可以是字符集裡的任一個. 舉例來說字符集 [abcwxyz] 會符合這七個字符中的任何一個. 為了方便你可以使用連字符 (-) 來指定某個範圍的字符 ; 因此上述字符集也可以寫成 [a-cw-z], 然而這麼做並不會省掉多少打字的功夫. 比較常見用法就是建立一個像 [a-zA-Z] 這樣的字符集, 來比對 52 個字母中的一個.
定義字符集時, 你可以使用與 "雙引號內字串" 相同的字符簡寫, 因此字符集 [\000-\177] 將會符合任何 7 位元的 ASCII 字符. 簡單範例如下 :
  1. $_ = "The HAL-900 requires authorization to continue.";  
  2. if(/HAL-[0-9]+/) {  
  3.     print "Match HAL Type!\n";  
  4. }  
有時候指定字符集以外的字符會比定字符集來的容易. 在字符集開頭加上脫字符號 (^) 可用來排除字符集. 也就是說 [^def] 會符合這三個字符以外的任何字符, 而 [^n\-z] 則會符合 "n", 連字符與 "Z" 以外的任何字符.

- 字符集簡寫
某些字符集十分常用, 因此具有自己的簡寫. 比方說代表任意數字的字符集 [0-9] 可以簡寫成 \d. 因此上面提到之 HAL 範例的樣式, 可以改寫成 /HAL-\d+/.
簡寫 \w 就是所謂的 "單字" (word) 字符 : [A-Za-z0-9_]. 如果你用的單字是由普通的字母, 數字與底線符號組成, 將可以使用 \w 來取代. 當然 \w 並不會符合一個單字 ; 它只會符合 "單字" 裡的一個字符. 雖然如此, 要比對完整的單字時, 只要使用加號修飾符就行了. 例如樣式 /fred \w+ barney/ 可用於比對 "fred, 一個空格,一個單字,一個空格,barney".
\s 簡寫擅長處理空白 ; 它相當於[\f\t\n\r ] . 也就是說, 它等於是個字符集, 裡面包含五種空白字符 : 跳頁 (\f), 跳格(\t), 換列(\n), 回行首(\r) 與空格.

- 簡寫的排除
有時候你也許想使用以上三種簡寫的反義. 這時你只需要將之轉成對應的大寫如 \D, \W 與 \S 就行了. 它們所符合的字符就是相應小寫形式所不符合的字符. 這些簡寫既可以做為樣式的獨立字符集, 也可以用為方括號字符集的一部份. 也就是說 /[\dA-Fa-f]+/ 可以用來比對十六進制數值. 另一個複合字符集是 [\d\D], 用來表示任何數字或非數字. 也就是說它會符合任何字符! 這是比對任意字符(包括換列字符)的常見做法.

[工具收集] Architects Regression Tester
regexlib.com :
Welcome to RegExLib.com, the Internet's first Regular Expression Library. Currently we have indexed 2970 expressions from 1748 contributors around the world. We hope you'll find this site useful and come back whenever you need help writing an expression, you're looking for an expression for a particular task, or are ready to contribute new expressions you’ve just figured out. Thanks!
This message was edited 4 times. Last update was at 25/08/2010 22:29:14

沒有留言:

張貼留言

網誌存檔