正規表示法(Regular Expressions)
正規表示法並不是Perl所獨有的,但在Perl裡卻經常被拿來使用。
這也是為什麼很多人聽到Perl,直覺都會想到它的文字處理能力。
正規表示法。簡單來說就是比對樣式(pattern),
依照需求而去「尋找」我們所要的字串,並對這些字串加以處理。
比對符號是用等號波浪號(=~)來表示,
一樣我們直接從例子來看會比較清楚:
$string = "No pain, no gain.";# 給予一段字串 並存到變數裡
if ($string =~ /gain/)# 當$string裡面有gain時
{
print "match!";# 印出match訊息
}
else # 若否
{
print "sorry!";# 印出sorry訊息
}
執行一次結果就會是: match!
知道簡單的比對樣式(pattern)後,
來看看一些不一樣的用法吧!
有的時候我們需要找的東西不只一種,
比方說我要找美妝用品,但是只想看兩種品項的文章,
就可以把if內的條件寫成這樣:
if ($string =~ /(化妝水|面膜)/)# 當$string裡面有化妝水 或是(|符號)面膜
當然,也可以指定說,關鍵字一定都要符合的,
像是我只要某些牌子的手機:
if ($string =~ /Apple/ && $string =~ /phone/)# 當$string裡面有Apple 並且(&&符號)有phone
如此一來,找到的東西就只會同時有Apple以及phone。
值得一提的是,因為Perl正規表示法中,
樣式比對要考慮到大小寫、空格、換行、特殊字元等,
所以上面那行程式碼,萬一遇到apple的a是小寫時,
比對就會失敗,
要避免這個問題,我們可以使用一些「字符集合」,
我們使用中括號([])表示 「或者」 的意思:
if ($string =~ /[aA]pple/ && $string =~ /[pP]hone/)# 首字大小寫都允許
如此一來,就可以避開首字大小寫的問題。
不過,如果每個字首都要定義大小寫,恐怕會寫成:
$string=~ /(/a|b|c|d|.......|z|A|B|C|D|......|_|)/
整串寫完真的會很壯觀呢!
當然,「字符集合」仍然有更方便的用法。
$string=~ /[a-zA-Z]/
短短這段字符集合就可以取代上面那段看起來很壯觀的程式碼,
表示大小寫都可以算比對成功。
$string=~ /[!@#$%^&*()_+=]/
而這樣就可以比對特殊字元!
但是問題來了,如果我要比對的是這個連字符號(-)呢?
上面的例子我們看到連字符號(-)已經被拿來當作取出a-z,A-Z的各個字符,
因此會使用到跳脫字元(escape character),
$string=~ /[\-]/
使用反斜線符號(\)表示下一個字元要跳脫(表示不做特殊用途),
因此就可以抓到連字符號。
而如果我們不要某些字,可以會使用插入符號(^),
$string=~ /[^A]/
表示我不要有A字元的樣式,以此類推。
知道使用字符集合增加樣式比對的複雜性後,
Perl的正規表示法其實也有特殊字元可以使用,
像是剛剛這段 $string =~ /[a-zA-Z]/
就可以簡寫成$string =~ /\w/
而常用的一些Perl正規表示法的特殊字元有:
\w:就等同於[a-zA-Z]的字符集合。
\W:就等同於[^a-zA-Z]的字符集合。 (大寫表示非的意思)
\s:就等同於空白字元(space)。
\S:就等同於非空白字元的字符集合。
\d:就等同於[0~9]的字符集合。
\D:就等同於[^0~9]的字符集合。
當然,你也可以這麼使用,
假設我想比對的樣式,長度在5~10之間,
那麼一樣可以使用修飾字元,
使用的方式就是使用大擴號({})來表示:
$string =~ /\w{5,10}/
如此一來就可以抓到長度5到10的英文字。
再來我們要回來看看正規表示法的修飾字元,
一樣我們用例子來說明,
如果我想抓取一段句子中,perl這個字串,
那麼你會遇到一個問題,除了首字大寫(Perl)之外,
句子中可能會有全部小寫(perl),以及全部大寫(PERL)的情況,
那麼表示起來可能又會寫一大段,
這時候修飾字元就派上用場了:
$string=~ /perl/i
注意到了嗎?樣式比對後面加了一個 i 表示 ignore (忽略)
表示我要忽略大小寫,如此一來上面的問題就可簡單處理掉了!
其他修飾字元還有:
$string=~ /perl/s
樣式比對後面加了一個 s 表示我要進行跨行(跨越\n)做比對
$string=~ /perl/g
樣式比對後面加了一個 g 表示我要對樣式進行全面比對(Match globally)
$string=~ /perl/o
樣式比對後面加了一個 o 表示我只要比對一次(Match once)
當然,你也可以把這些修飾字元都一起使用:
$string=~ /perl/io
樣式比對後面加了一個 i 以及 o
表示我想要進行忽略大小寫的樣式比對,並且只要成功比對一次就好。
知道如何比對樣式後,有時我們也會需要取得比對結果,
在Perl的正規表示法裡,有預設變數來讓你取得比對的結果,
預設的變數是使用$1來接比對後的結果,
然而,如果還有其他比對結果,則會用到$2,
如果還有就會持續遞增:像是$1, $2, $3 ...
而要把我們想要取得的樣式存到這些變數裡,
方法也很簡單,就是用小括號()把這些樣式包起來。
一樣我們從例子來說明:
$string = "God helps those who help themselves.";# 給予一段字串 並存到變數裡
if ($string =~ /(God)(.*)(those)/i)# 當$string裡面有(God)以及(任何字元)還有(those)時
# 因為修飾字元有i 所以會忽略大小寫 並且 因為有三個小括號
# 所以成功比對的樣式會依序存入3個預設變數 即是 $1 $2 $3
{
print "$1 \n";# 印出$1以及換行
print "$2 \n";# 印出$2以及換行
print "$3 \n";# 印出$3以及換行
}
執行結果就會看到,
這些比對的樣式依序被抓取出來。
$1裡面存的就會是"God"
$2裡面存的就會是" helps " (空格也會被抓進來)
$3裡面存的就會是"those"
接下來我們來看看更精確的描述正規表示式,
我們會使用到的是定位點。
透過定位點,可以表示我要找的樣式是在開頭,或是在結尾。
$string=~ /^perl/
插入符號(^)在這裡表示以這個樣式「開頭」
需要注意的是,在這裡所使用的插入符號(^)跟「字符集合」意思不同,
一個表示「開頭」,一個表示「非」,可別搞混了!
$string=~ /perl$/
錢字符號($)在這裡表示以這個樣式「結尾」
再來是正規表示法的比對與替換。
如果我們要把比對好的結果作替換,
寫法稍微有點不同,格式會長得像這樣:
$string=~ s/原本的樣式/替換的樣式/
利用三個反斜線夾住要比對以及替換的樣式,
並且在前面加上一個 s 表示 substitute (替換)。
後面一樣可以加上一些修飾字元,像是上面提到的忽略大小寫(i)等等。
我們直接看例子:
$string = "我不喜歡吃苦瓜,我還是不喜歡吃苦瓜。";# 給予一段字串 並存到變數裡
$string =~ s/苦瓜/鹹蛋/gi; # 把苦瓜替換成鹹蛋 全部都換(g)並忽略大小寫(i)
print "$string";# 印出$string
執行結果可以看到所有的苦瓜,都被換成了鹹蛋!
最後是正規表示所用到的萬用字元(.)問題,
Perl的正規表示是貪多比對,
即是Perl預設會去找到符合需求的「最大集合」。
用例子看會比較清楚:
$string = "我不喜歡吃苦瓜,我還是不喜歡吃苦瓜,我真的不喜歡吃苦瓜。";
if ($string =~ /我不喜歡(.*)苦瓜/)# 取得夾在 我不喜歡 以及 苦瓜 之中的樣式
{
print "$1 \n";# 印出$1以及換行
}
執行以後會發現,
我們原本希望只抓到"吃",這一個字,
但Perl的預設就是找到「最大集合」,
這就是所謂的貪多比對。
如果要避開這個問題,我們會在比對的量詞後面,加上問號(?)來表示不貪多,
因此我們把上面的式子改寫成為:
$string = "我不喜歡吃苦瓜,我還是不喜歡吃苦瓜,我真的不喜歡吃苦瓜。";
if ($string =~ /我不喜歡(.*?)苦瓜/)# 用問號表示不貪多比對
{
print "$1 \n";# 印出$1以及換行
}
如此一來,執行結果就會符合我們的期待
正規表示式就到這邊結束,接下來要回到控制敘述的迴圈操作。