文章內文:
Regular Expression (正規表示式,簡寫 regexp或regex)
是一個字串,可用來描述字串的模式,
例如我要表達「兩個英文大寫字母」,那麼 regexp 就是 [A-Z]{2}。
而使用這個 regexp ,就可以在一個字串內找出配合 (Match) 的部份,
例如在字串 $st = '11AA22BB33' 內,有哪些部份配合「兩個英文大寫字母」這個模式?
就會找到 'AA' 和 'BB' 兩部份了。
以下是一些常見的 regexp 應用:
use strict;
my ($st, $pattern, $replacement, @array, @matches);
##
## 有沒有找到至少一個 Match?
##
$st = '11AA22';
$pattern = 'AA';
if ($st =~ /$pattern/) {
print "has at least one match\n";
} else {
print "no match\n";
}
##
## 找出所有 Matches
##
$st = '11AA22BB33';
$pattern = '[A-Z]{2}';
@matches = $st =~ /$pattern/g;
foreach my $i (0 .. $#matches) {
print "$i : $matches[$i]\n";
}
##
## 找出所有 Matches,並取代它們
##
$st = '11AA22AA33';
$pattern = 'AA';
$replacement = 'XX';
$st =~ s/$pattern/$replacement/g;
print "$st\n";
##
## 在 Matches 的位置分割
##
$st = '11AA22AA33';
$pattern = 'AA';
@array = split /$pattern/, $st;
print join ',', @array;
以下是 .Net 例子(以 C# 語言)
using System;
using System.Text.RegularExpressions;
class MainClass
{
public static void Main(string[] args)
{
string st, pattern, replacement;
string[] array;
MatchCollection mc;
//
// has at least one match?
//
st = "11AA22";
pattern = @"AA";
if ( Regex.IsMatch(st, pattern) ) {
Console.WriteLine("has at least one match");
} else {
Console.WriteLine("no match");
}
//
// get all matches
//
st = "11AA22BB33";
pattern = @"[A-Z]{2}";
mc = Regex.Matches(st, pattern);
for (int i = 0; i < mc.Count; i++ )
{
Console.WriteLine("{0} : {1}", i, mc);
}
//
// search and replace
//
st = "11AA22AA33"; pattern = @"AA"; replacement = "XX";
st = Regex.Replace(st, pattern, replacement);
Console.WriteLine("{0}", st);
//
// split
//
st = "11AA22AA33"; pattern = "AA";
array = Regex.Split(st, pattern);
Console.WriteLine(String.Join(",", array));
}
}
[[解釋 regexp 的意思]]
當您遇到一些很難看得懂的 regexp, 您可以用 YAPE::Regex::Explain 模組, 把 regexp 翻譯成英文,這對新手來說有很大幫助。
#### use YAPE::Regex::Explain;
my $pattern = '[A-Z]{2}';
print YAPE::Regex::Explain->new($pattern)->explain;
####
輸出結果:
[A-Z]{2} any character of: 'A' to 'Z' (2 times)
意思是「任何一個由 'A' 至 'Z' 的字元(兩次)」,
那麼它配合的東西就是:
AA
AB
...
AZ
BA
BB
...
ZA
ZB
...
ZZ
------------------------------------------------------------------------------------------------
>我目前所碰到的瓶頸是
>有沒有可能使用Regular Expression來判斷輸入的是數字,並且限定其數值範圍0.0
參考 perlretut 內的 "Building a regexp" 一節
------------------------------------------------------------------------------------------------
[[ 特殊字元 (Metacharacter) ]]
當您要表示一些特殊字元,例如括號,那就不能直接寫:
$pattern = '(';
而要在前面加上反斜號:
$pattern = '\(';
以下 regexp 的特殊字元:
{}[]()^$.|*+?/
為什麼這些是特殊字元呢?本文稍後會介紹。
例:
####
$st = '11((22';
$pattern = '\(\(';
@matches = $st =~ /$pattern/g;
foreach my $i (0 .. $#matches) {
print "$i : $matches[$i]\n";
}
####
順帶一提,應儘量用單引號來括字串,
因為它不會好像雙引號那樣會做字串插入,如果寫:
$pattern = "\(\(";
那結果變成:
$pattern = '((';
------------------------------------------------------------------------------------------------
[[ 脫離序列 (Escape Sequence) ]]
當您要表示 tab、 newline 等不能列印的字元,
可以用 Escape Sequence。
例:
$st = "11\t22\t33";
$pattern = '\t';
------------------------------------------------------------------------------------------------
[[ 字元類別 (Character Class) ]]
您除了可以表示一個固定的字元外,如
$pattern = 'A';
還可以表示一個字元集合,例如您想表示
「'A'字元或者'B'字元或者'C'字元」,可以寫
$pattern = '[ABC]';
即把您想 match 的字元寫在中括號內。
例:
$st = '11A22B33C44D';
$pattern = '[ABC]';
會找到以下的 match:
0 : A
1 : B
2 : C
例如您想找 'AAAXX' 或者 'AABXX' 或者 'AACXX':
$st = '11AACXX22AABXX33AAAXX44AAD';
$pattern = 'AA[ABC]XX';
會找到以下的 match:
0 : AACXX
1 : AABXX
2 : AAAXX
留意一點,雖然 $pattern 內的 [ABC] 是先寫A,
但結果是先找到 'AACXX',而不是'AAAXX',
因為 Perl 是先從 $st 的左邊開始找,而不是先從右邊開始找,
而 'AACXX' 是最左邊的 match。
------------------------------------------------------------------------------------------------
[[ 字元類別 - 脫離序列 ]]
在字元類別內表示一些特殊字元,也是要加反斜號,
不過字元類別內的特殊字元是:
-]\^$
本文稍後會解釋為何它們是特殊字元。
例:
$st = '11A22B33-44]';
$pattern = '[AB\-\]]';
會找到以下的 match:
0 : A
1 : B
2 : -
3 : ]
------------------------------------------------------------------------------------------------
[[ 字元類別 - 範圍運算子 (Range Operator) ]]
您可以用減號,來表示一段字元範圍,例如
$pattern = '[ABCDEFGHIJ]';
可以寫成
$pattern = '[A-J]';
這樣就更簡潔了。又例如
$pattern = '[ABCDKLMN0123]';
可以寫成
$pattern = '[A-DK-N0-3]';
但如果把減號寫在最頭或者最尾,例如
$pattern = '[-AB]';
或
$pattern = '[AB-]';
那麼減號就直接表示 '-' 字元,而不是範圍運算子了。
例:
$st = '99A88L773]';
$pattern = '[A-DK-N0-3]';
會找到以下的 match:
0 : A
1 : L
2 : 3
------------------------------------------------------------------------------------------------
[[ 字元類別 - 反相字元類別 (Negated Character Class) ]]
例如要表示「'A'或者'B'或者'C'」,就寫
$pattern = '[ABC]';
但如果要表示「'A'或者'B'或者'C'」的相反,即「不是'A'並且不是'B'並且不是'C'」
就可以寫
$pattern = '[^ABC]';
在字元類別內,如果第一個字元是 '^',就表示這是一個「反相字元類別」。
例:
$st = '11A22B33X44Y';
$pattern = '[^A-C0-9]';
會找到以下的 match:
0 : X
1 : Y
------------------------------------------------------------------------------------------------
[[ 字元類別 - 簡寫 ]]
Perl 提供了一些常用的字元類別簡寫,例如
$pattern = '[0-9]';
其實可以寫成
$pattern = '\d';
這就更簡潔了。以下列出這些簡寫:
\d 等於寫 [0-9]
\D 等於寫 [^0-9] ,即 \d 的相反
\w 等於寫 [0-9a-zA-Z_] ,即數字、英文大小寫字母和底線
\W 等於寫 [^0-9a-zA-Z_] ,即 \w 的相反
\s 等於寫 [\ \t\n\r\f] ,即白格字元 (Whitespace character),
包括space, tab, newline, carriage return, form feed
\S 等於寫 [^\ \t\n\r\f] ,即 \s 的相反
還有一個很特別的簡寫,就是點號 '.',
因為它比較複雜,所以稍後再介紹它。
例:
$st = '11A22b33_44=';
$pattern = '\D';
會找到以下的 match:
0 : A
1 : b
2 : _
3 : =
例:
$st = '11AA22A_33A044AX';
$pattern = 'A[^\dX-Z]';
會找到以下的 match:
0 : AA
1 : A_
------------------------------------------------------------------------------------------------
[[ Alternation ]]
如果想表示「'AB'字串或'KLM'字串或者'PQRST'」,可以寫
$pattern = 'AB|KLM|PQRST';
即用直線分隔它們。
Alternation 跟 Character Class 很像,
例如以下兩者是一樣的:
$pattern = '[ABC]';
$pattern = 'A|B|C';
不過 Character Class 不能表示超過一個字元的字串。
例:
$st = '11AB22KLMN33PQRS';
$pattern = 'AB|KLM|PQRST';
會找到以下的 match:
0 : AB
1 : KLM
如果寫:
$pattern = 'PQRST|KLM|AB';
那還是會先找到 'AB',效果跟 Character Class 一樣。
------------------------------------------------------------------------------------------------
[[群組 (Group)]]
如果想表示「'ABC'或者'ABKL'或者'ABPQR'」,除了可以寫:
$pattern = 'ABC|ABKL|ABPQR';
還可以這樣寫:
$pattern = 'AB(C|KL|PQR)';
即用括號來分開'AB'和接著的那三個 Alternatives,
好像數學上把 2*3 + 2*4 + 2*5 寫成 2 * (3+4+5)。
如果想連'AB'都要 match:
$pattern = 'AB|ABC|ABKL|ABPQR';
也可以把空字串都寫到括號內:
$pattern = 'AB(C|KL|PQR|)';
------------------------------------------------------------------------------------------------
[[群組 - 特殊變數 $1, $2, ...]]
括號還有一種特別的功能,就是把括號內 match 到的部份
放到特殊變數 $1, $2, ...。
例如您想檢查某字串是含有時間的模式,例如 HH:MM:SS,
(不檢查數值範圍),同時想取得時分秒各個部份,可以寫:
####
my ($time_string, $hour, $minute, $second);
$time_string = '12:34:56';
$pattern = '(\d\d):(\d\d):(\d\d)';
if ($time_string =~ /$pattern/) {
($hour, $minute, $second) = ($1, $2, $3);
print "hour=$hour,minute=$minute,second=$second";
} else {
print 'Invalid time format';
}
####
執行結果:
hour=12,minute=34,second=56
至於哪部份是 $1, $2, ...,就以開括號出現的先後順序來決定,
例如括著「時」那個開括號是第一個出現的,所以它就對應 $1。
(\d\d):(\d\d):(\d\d)
1______2______3
------------------------------------------------------------------------------------------------
[[群組 - 巢狀群組]]
括號內也可以有括號,成為巢狀群組,例如:
(\d\d):((\d\d):(\d\d))
1______23______4
####
my ($time_string, $hour, $minute, $second);
$time_string = '12:34:56';
$pattern = '(\d\d):((\d\d):(\d\d))';
if ($time_string =~ /$pattern/) {
print "\$1=$1\n\$2=$2\n\$3=$3\n\$4=$4";
} else {
print 'Invalid time format';
}
####
執行結果:
$1=12
$2=34:56
$3=34
$4=56
------------------------------------------------------------------------------------------------
[[群組 - 返回參照 (Backreferences) \1, \2, ...]]
跟 $1, $2, ... 有很密切關係的東西,就是 Backreferences,
即 \1, \2, ... 。
它們不同之處,在於 $1 只應該用在 regexp 「以外」,
而 \1 只應該用在 regexp 「以內」。
例:
####
$st = 'A_A';
$pattern = '([ABC])_\1';
if ($st =~ /$pattern/) {
print "\$1=$1";
}
####
執行結果:
$1=A
您可以嘗試把 $st 改成:
$st = 'B_B';
$st = 'C_C';
您會發現 \1 能夠取得之前所 match 的東西,
即應該放到 $1 的值。但要緊記,不應該在 regexp 以內寫 $1。
例:
####
$st = 'ALZ_A_L_Z';
$pattern = '([ABC])([KLM])([XYZ])_\1_\2_\3';
if ($st =~ /$pattern/) {
print "\$1=$1\n\$2=$2\n\$3=$3";
}
####
執行結果:
$1=A
$2=L
$3=Z
------------------------------------------------------------------------------------------------
[[ 群組 - $1, $2, ... 的開始和結束位置 ]]
Perl 5.6.0 提供了兩個陣列 @- 和 @+,
@- 儲存了 $1, $2, ... 的開始位置,@+ 就儲存結束位置。
例:
####
my ($time_string, $hour, $minute, $second);
$time_string = '12:34:56';
$pattern = '(\d\d):(\d\d):(\d\d)';
$time_string =~ /$pattern/;
foreach my $i (1 .. $#-) {
print "Match $i='${$i}' at position ($-[$i] , $+[$i])\n";
}
####
執行結果:
Match 1='12' at position (0 , 2)
Match 2='34' at position (3 , 5)
Match 3='56' at position (6 , 8)
------------------------------------------------------------------------------------------------
[[ 群組 - $` 、 $& 和 $' ]]
如果 regexp 沒有寫括號,就沒有 $1, $2, ... 可用了,
但您仍然可以用 $& 來取得 match,$` 則取得 match 前面所有東西,
$' 取得 match 後面所有東西。
####
$st = '11AA22';
$pattern = 'AA';
$st =~ /$pattern/;
print "\$`=$`\n\$&=$&\n\$'=$'";
####
執行結果:
$`=11
$&=AA
$'=22
如果用 substr 來表示,那麼:
$` 是 substr( $st, 0, $-[0] )
$& 是 substr( $st, $-[0], $+[0] - $-[0] )
$' 是 substr( $st, $+[0] )
------------------------------------------------------------------------------------------------
[[ 重覆 (Repetition) ]]
如果想表示「五個 'A' 字元」,可以寫
$pattern = 'AAAAA';
也可以用量化詞 (Quantifier) ,寫成
$pattern = 'A{5}';
這樣就更清楚了。而量化詞可以寫在字元、字元類別或群組的後面。
例:
$st = '11AAA22AAAAA';
$pattern = 'A{5}';
------------------------------------------------------------------------------------------------
[[重覆 - 最多和最少]]
量化詞可分成兩類:
1) 最多配對(貪心的) Maximal Match (Greedy)
2) 最少配對(不貪心的) Minimal Match (Non-greedy)
以下列出「最多配對」:
A? 表示 0 個或 1 個 'A' 字元(以最多為準)
A* 表示 0 個或以上的 'A' 字元(以最多為準)
A+ 表示 1 個或以上的 'A' 字元(以最多為準)
A{n} 表示 n 個 'A' 字元
A{n,m} 表示最少 n 個,最多 m 個 'A' 字元(以最多為準)
A{n,} 表示最少 n 個 'A' 字元(以最多為準)
以下列出「最少配對」:
A?? 表示 0 個或 1 個 'A' 字元(以最少為準)
A*? 表示 0 個或以上的 'A' 字元(以最少為準)
A+? 表示 1 個或以上的 'A' 字元(以最少為準)
A{n}? 表示 n 個 'A' 字元
(這個其實沒有所謂的「最多」還是「最少」之分,
定義這個符號只是為了保持符號一致性而己)
A{n, m}? 表示最少 n 個,最多 m 個 'A' 字元(以最少為準)
A{n,}? 表示最少 n 個 'A' 字元(以最少為準)
到底「最多配對」和「最少配對」有什麼分別呢?例如
$st = 'AAAAA';
$pattern1 = 'A{3,5}'; # 最多配對
$pattern2 = 'A{3,5}?'; # 最少配對
那麼 match 可能是:
1) 'AAA'(字元位置0至2)
2) 'AAAA'(字元位置0至3)
3) 'AAAAA'(字元位置0至4)
如果是「最多配對」,那麼 match 就是 'AAAAA',因為它是最多次數,
如果是「最少配對」,那麼 match 就是 'AAA',因為它是最少次數。
------------------------------------------------------------------------------------------------
[[ 特點字元 - 任何一個字元 ]]
如果要表示「任何一個字元」,可以用點號 '.'。
在預設的情況下,它會配到「任何一個字元,除了 "\n" 之外」:
$st = "123ABC_!@\n";
$pattern = '.';
@matches = $st =~ /$pattern/g;
如果要配到「任何一個字元,包括 "\n"」,
就到加入 s 這個「修飾詞」(Modifier):
@matches = $st =~ /$pattern/sg;
s 的意思是「把字串當作單行 (Single Line)」。
------------------------------------------------------------------------------------------------
[[ 字串的開頭 ]]
如果要表示「字串的開頭」,可以用 '^',例如:
$st = 'AA22';
$pattern = '^AA';
@matches = $st =~ /$pattern/g;
執行結果:
0 : AA
但 match 當中不會有 '^' 所配出來的字元,
因為 '^' 主要用來測試字串的特性(例如字串位置),
而不是配任何字元的。
為了方便理解,您可以把它「想像」成一個看得見的字元,
例如:
$st = '頭AA22尾'; ## $st = 'AA22'
$pattern = '頭AA'; ## $pattern = '^AA';
------------------------------------------------------------------------------------------------
[[ 字串的結尾 ]]
如果要表示「字串的結尾,或者在字串最尾的 \n 和前面字元的中間」,
可以用 '$',例如:
$st = "AA\nBB\n"; ## 用了雙引號
$pattern = '$';
@matches = $st =~ /$pattern/g;
結果有兩個 match ,但是'$' 與 '^' 一樣,是不會配到字元的。
您可以把它想像成:
$st = "頭AA\nBB尾\n尾"; ## $st = "AA\nBB\n";
$pattern = '尾'; ## $pattern = '$';
另外,當檢查一個字串是否符合某種格式時,
通常會 '^' 和 '$' 一起用,
例如檢查某字串是否符合「三位數字」的格式:
####
$st = "123";
$pattern = '^\d{3}$';
if ($st =~ /$pattern/) {
print "Valid format";
} else {
print "Invalid format";
}
####
------------------------------------------------------------------------------------------------
[[ 多行 (Multi-line) ]]
一個字串內可以含有 \n,例如:
$st = "AA\nAA\nAA\n";
$pattern = '^AA';
如果我們想 '^' 配到每一行的開頭,
可以用 m 修飾詞:
@matches = $st =~ /$pattern/mg;
執行結果:
0 : AA
1 : AA
2 : AA
想像成:
$st = "頭AA尾\n頭AA尾\n頭AA尾\n尾";
$pattern = '頭AA';
用了 m 修飾詞,也會令 '$' 配到每一行的結尾,
$st = "AA\nAA\nAA\n";
$pattern = 'AA$';
@matches = $st =~ /$pattern/mg;
想像成:
$st = "頭AA尾\n頭AA尾\n頭AA尾\n尾";
$pattern = 'AA尾';
如果用了 m 修飾詞,您仍然可以用:
\A 表示沒有 m 修飾詞的 '^' 意思
\Z 表示沒有 m 修飾詞的 '$' 意思
\z 代表字串的結尾(即 \Z ,但不包括最尾 \n 和前面字元的中間)
$st = "AA\nAA\nAA\n";
$pattern1 = '\AAA';
$pattern2 = 'AA\Z';
$pattern3 = 'AA\z';
總括來說,s 修飾詞會影響 '.' 的意思,
m 修飾詞會影響 '^' 和 '$' 的意思,
s 和 m 總共有四種可能:
沒有 s 和 m :預設模式 (Default Mode)
/s :單行模式 (Single Line Mode)
/m :多行模式 (Multi-Line Mode)
/sm :清楚多行模式 (Clean Multi-Line Mode)
看看大家能否解釋以下程式的執行結果:
####
$st = "A\nA\nA\n";
$pattern = '(^|$|.)';
@matches = $st =~ /$pattern/smg;
####
------------------------------------------------------------------------------------------------
[[ 字的邊界 Word Boundary ]]
字的邊界是指兩個連續字元的中間,
一個會配到 \w,而另一個會配到 \W。
(字串的開頭和結尾也視為配到 \W)
例如:
$st = 'ABC-=+';
$pattern = 'C\b';
@matches = $st =~ /$pattern/g;
因為左邊的 'C' 會配到 \w,而右邊的 '-' 會配到 \W,
所以 \b 會配到它們的中間。
\b 不代表某個字元,而是代表某個位置的特性。
您可以想像成:
$st = '界ABC界-=+';
$pattern = 'C界';
您可以用 \B 來表示 \b 的相反,即「不是邊界」,
例如:
$st = 'ABCDE';
$pattern = '\BC\B';
@matches = $st =~ /$pattern/g;
因為 'C' 本身配到 \w ,而它左邊的'B'和右邊的'D'也是配到 \w,
所以沒有邊界。
------------------------------------------------------------------------------------------------
[[ 不分大小寫 (Case-insensitive) ]]
如果加入了 i 修飾詞,就會做不分大小寫的配對,
例如:
$st = '11AA22BB';
$pattern = '[abAB]';
@matches = $st =~ /$pattern/g;
可以用 i 修飾詞寫成:
$st = '11AA22BB';
$pattern = '[AB]'; # 或者 '[ab]'
@matches = $st =~ /$pattern/ig;
因為不分大小寫,所以不必在 $pattern 把大小寫都列出來了。
------------------------------------------------------------------------------------------------
[[ x 修飾詞 ]]
如果用了 x 修飾詞,就會令 regexp 解譯器忽略 $pattern 內的白格,
(除了那些在被反斜號標示了的白格,及那些在字元類別內的白格)
還有在 $pattern 內的 '#' 也會有好像 Perl 一樣的單行註解作用,
(除了那些在被反斜號標示了的'#',及那些在字元類別內的'#')
它的用途是讓您可以把 regexp 寫成容易閱讀的方式,及加入註解。
例子:
不用 x 修飾詞的寫法:
$st = '11AA22 33\\\\44##';
$pattern = '(A| |\\\\|#)';
@matches = $st =~ /$pattern/g;
用了 x 修飾詞的寫法:
$st = '11AA22 33\\\\44##';
$pattern = q{
( # 左括號表示 Alternation 開始
A # 'A' 字元
| # 或者
[ ] # 用字元類別表示空格
| # 或者
\\\\ # 反斜號字元
|
[#] # 用字元類別表示'#'
) # 右括號表示完結
};
@matches = $st =~ /$pattern/xg;
------------------------------------------------------------------------------------------------
[[ 伸展模式 (Extended Patterns) ]]
除了傳統的 regexp 語法外,Perl 還定義了一些額外的語法,
但在其它工具未必有這些語法,而且有些語法可能還在實驗階段,
說不定將來會被刪除,詳情請參閱說明文件。
這些語法都是這樣子的:
(?char)
而 char 就表示伸展的種類。
------------------------------------------------------------------------------------------------
[[ 伸展模式 - 內嵌註解 (Embedding Comments) ]]
語法:
(?#text)
在 text 位置的東西會當作註解,
但要留意 text 內不可以含有右括號。
例:
$pattern = '(?# THIS IS A COMMENT)(A| |\\\\|#)';
------------------------------------------------------------------------------------------------
[[ 伸展模式 - 非捕捉群組 (Non-capturing groupings) ]]
語法:
(?:pattern)
之前提到括號有個特別的作用,就是會把配到的東西放進 $1, $2,...,
例如:
$time_string = '12:34:56';
$pattern = '(\d\d):(\d\d):(\d\d)';
if ($time_string =~ /$pattern/) {
($hour, $minute, $second) = ($1, $2, $3);
print "hour=$hour,minute=$minute,second=$second";
}
但如果您不想放的話,可以用 (?:pattern):
$time_string = '12:34:56';
$pattern = '(\d\d):(?:\d\d):(\d\d)';
if ($time_string =~ /$pattern/) {
($hour, $minute, $second) = ($1, $2, $3);
print "hour=$hour,minute=$minute,second=$second";
}
因為「分」那部份使用了(?:pattern) ,所以就不會放到本來的 $2,
這就輪到「秒」的部份放到 $2了。
------------------------------------------------------------------------------------------------
[[ 伸展模式 - 內嵌修飾詞 (Embedded Modifiers) ]]
語法:
(?imsx-imsx:pattern)
之前提到可以用 i 修飾詞來做不分大小寫的配對,例如:
$st = '11AB22Ab33aB44ab';
$pattern = 'AB';
@matches = $st =~ /$pattern/ig;
這樣整個 $pattern 都是不分大小寫了,
如果想某部份分辨大小寫,某部份不分辨,
可以用內嵌的寫法。
$st = '11AB22Ab33aB44ab';
$pattern = '(?i:A)(?-i:B)';
@matches = $st =~ /$pattern/g;
表示 A 不分大小寫,B 就分辨大小寫。
如果修飾詞前面是減號,表示關閉該修飾詞,
例如 (?s-i),表示啟動 s 修飾詞,關閉 i 修飾詞。
其實內嵌修飾詞的語法,是在非捕捉群組內加入修飾詞而己。
------------------------------------------------------------------------------------------------
[[ 伸展模式 - 往前看(Look-ahead)和往後看(Look-behind) ]]
語法:
往前看(正)(Positive Look-ahead) (?=pattern)
往後看(正)(Positive Look-behind) (?<=pattern)
往前看(負)(Negative Look-ahead) (?!pattern)
往後看(負)(Negative Look-behind) (?
舉例來說:
$st = 'ABCDEFGH';
$pattern = '\w{2}';
@matches = $st =~ /$pattern/g;
執行結果:
0 : AB
1 : CD
2 : EF
3 : GH
您可以想像它是由左往右走,當配到一個match,
下一次就由match之後開始走:
ABCDEFGH
由A的位置開始走,配到 AB,剩下 CDEFGH ('AB')
由C的位置開始走,配到 CD,剩下 EFGH ('AB', 'CD')
由E的位置開始走,配到 EF,剩下 GH ('AB', 'CD', 'EF')
由G的位置開始走,配到 GH,剩下 ('AB', 'CD', 'EF', 'GH')
如果想在走路的時候,還要同時往前面看,
可以用「往前看」或「往後看」。
例如仍然要找出 \w{2},但同時要檢查它的前面(即右面)必須是\w{2},
可以寫:
$pattern = '\w{2}(?=\w{2})';
執行結果:
0 : AB
1 : CD
2 : EF
這裡的 (?=\w{2}) 用了「往前看(正)」,
這部份是不會配到任何字元,只是用來檢查字串的特性:
ABCDEFGH
由A的位置開始,配到AB,往前看到CD,剩下 CDEFGH ('AB')
由C的位置開始,配到CD,往前看到EF,剩下 EFGH ('AB', 'CD')
由E的位置開始,配到EF,往前看到GH,剩下 GH ('AB', 'CD', 'EF')
由G的位置開始,雖然GH 可以配到\w{2} ,但往前看不到 \w{2},所以沒有match
完結
同理,如果改用「往後看」:
$pattern = '(?<=\w{2})\w{2}';
執行結果:
0 : CD
1 : EF
2 : GH
ABCDEFGH
由A的位置開始,直至走到C的位置時,
才能夠往後看到 AB,配到CD,剩下 EFGH ('CD')
由E的位置開始,往後看到CD,配到EF,剩下 GH ('CD', 'EF')
由G的位置開始,往後看到EF,配到GH,剩下 ('CD', 'EF', 'GH')
完結
Negative Look-ahead 是 Positve Look-ahead的相反,
例如仍然要找出 \w{2},但同時要檢查它的前面必須「不是」\w{2},
可以寫:
$st = 'ABCDEFGH';
$pattern = '\w{2}(?!\w{2})';
@matches = $st =~ /$pattern/g;
執行結果:
0 : FG
因為 'FG'的右面只有 'H' ,即 \w ,而不是 \w{2},
所以配到。
原文連結:
http://ck101.com/forums/viewthread.php?tid=1270923&extra=page%3D1%26amp%3Bfilter%3Dtype%26amp%3Btypeid%3D29
Regular Expression (正規表示式,簡寫 regexp或regex)
是一個字串,可用來描述字串的模式,
例如我要表達「兩個英文大寫字母」,那麼 regexp 就是 [A-Z]{2}。
而使用這個 regexp ,就可以在一個字串內找出配合 (Match) 的部份,
例如在字串 $st = '11AA22BB33' 內,有哪些部份配合「兩個英文大寫字母」這個模式?
就會找到 'AA' 和 'BB' 兩部份了。
以下是一些常見的 regexp 應用:
use strict;
my ($st, $pattern, $replacement, @array, @matches);
##
## 有沒有找到至少一個 Match?
##
$st = '11AA22';
$pattern = 'AA';
if ($st =~ /$pattern/) {
print "has at least one match\n";
} else {
print "no match\n";
}
##
## 找出所有 Matches
##
$st = '11AA22BB33';
$pattern = '[A-Z]{2}';
@matches = $st =~ /$pattern/g;
foreach my $i (0 .. $#matches) {
print "$i : $matches[$i]\n";
}
##
## 找出所有 Matches,並取代它們
##
$st = '11AA22AA33';
$pattern = 'AA';
$replacement = 'XX';
$st =~ s/$pattern/$replacement/g;
print "$st\n";
##
## 在 Matches 的位置分割
##
$st = '11AA22AA33';
$pattern = 'AA';
@array = split /$pattern/, $st;
print join ',', @array;
以下是 .Net 例子(以 C# 語言)
using System;
using System.Text.RegularExpressions;
class MainClass
{
public static void Main(string[] args)
{
string st, pattern, replacement;
string[] array;
MatchCollection mc;
//
// has at least one match?
//
st = "11AA22";
pattern = @"AA";
if ( Regex.IsMatch(st, pattern) ) {
Console.WriteLine("has at least one match");
} else {
Console.WriteLine("no match");
}
//
// get all matches
//
st = "11AA22BB33";
pattern = @"[A-Z]{2}";
mc = Regex.Matches(st, pattern);
for (int i = 0; i < mc.Count; i++ )
{
Console.WriteLine("{0} : {1}", i, mc);
}
//
// search and replace
//
st = "11AA22AA33"; pattern = @"AA"; replacement = "XX";
st = Regex.Replace(st, pattern, replacement);
Console.WriteLine("{0}", st);
//
// split
//
st = "11AA22AA33"; pattern = "AA";
array = Regex.Split(st, pattern);
Console.WriteLine(String.Join(",", array));
}
}
[[解釋 regexp 的意思]]
當您遇到一些很難看得懂的 regexp, 您可以用 YAPE::Regex::Explain 模組, 把 regexp 翻譯成英文,這對新手來說有很大幫助。
#### use YAPE::Regex::Explain;
my $pattern = '[A-Z]{2}';
print YAPE::Regex::Explain->new($pattern)->explain;
####
輸出結果:
[A-Z]{2} any character of: 'A' to 'Z' (2 times)
意思是「任何一個由 'A' 至 'Z' 的字元(兩次)」,
那麼它配合的東西就是:
AA
AB
...
AZ
BA
BB
...
ZA
ZB
...
ZZ
------------------------------------------------------------------------------------------------
>我目前所碰到的瓶頸是
>有沒有可能使用Regular Expression來判斷輸入的是數字,並且限定其數值範圍0.0
參考 perlretut 內的 "Building a regexp" 一節
------------------------------------------------------------------------------------------------
[[ 特殊字元 (Metacharacter) ]]
當您要表示一些特殊字元,例如括號,那就不能直接寫:
$pattern = '(';
而要在前面加上反斜號:
$pattern = '\(';
以下 regexp 的特殊字元:
{}[]()^$.|*+?/
為什麼這些是特殊字元呢?本文稍後會介紹。
例:
####
$st = '11((22';
$pattern = '\(\(';
@matches = $st =~ /$pattern/g;
foreach my $i (0 .. $#matches) {
print "$i : $matches[$i]\n";
}
####
順帶一提,應儘量用單引號來括字串,
因為它不會好像雙引號那樣會做字串插入,如果寫:
$pattern = "\(\(";
那結果變成:
$pattern = '((';
------------------------------------------------------------------------------------------------
[[ 脫離序列 (Escape Sequence) ]]
當您要表示 tab、 newline 等不能列印的字元,
可以用 Escape Sequence。
例:
$st = "11\t22\t33";
$pattern = '\t';
------------------------------------------------------------------------------------------------
[[ 字元類別 (Character Class) ]]
您除了可以表示一個固定的字元外,如
$pattern = 'A';
還可以表示一個字元集合,例如您想表示
「'A'字元或者'B'字元或者'C'字元」,可以寫
$pattern = '[ABC]';
即把您想 match 的字元寫在中括號內。
例:
$st = '11A22B33C44D';
$pattern = '[ABC]';
會找到以下的 match:
0 : A
1 : B
2 : C
例如您想找 'AAAXX' 或者 'AABXX' 或者 'AACXX':
$st = '11AACXX22AABXX33AAAXX44AAD';
$pattern = 'AA[ABC]XX';
會找到以下的 match:
0 : AACXX
1 : AABXX
2 : AAAXX
留意一點,雖然 $pattern 內的 [ABC] 是先寫A,
但結果是先找到 'AACXX',而不是'AAAXX',
因為 Perl 是先從 $st 的左邊開始找,而不是先從右邊開始找,
而 'AACXX' 是最左邊的 match。
------------------------------------------------------------------------------------------------
[[ 字元類別 - 脫離序列 ]]
在字元類別內表示一些特殊字元,也是要加反斜號,
不過字元類別內的特殊字元是:
-]\^$
本文稍後會解釋為何它們是特殊字元。
例:
$st = '11A22B33-44]';
$pattern = '[AB\-\]]';
會找到以下的 match:
0 : A
1 : B
2 : -
3 : ]
------------------------------------------------------------------------------------------------
[[ 字元類別 - 範圍運算子 (Range Operator) ]]
您可以用減號,來表示一段字元範圍,例如
$pattern = '[ABCDEFGHIJ]';
可以寫成
$pattern = '[A-J]';
這樣就更簡潔了。又例如
$pattern = '[ABCDKLMN0123]';
可以寫成
$pattern = '[A-DK-N0-3]';
但如果把減號寫在最頭或者最尾,例如
$pattern = '[-AB]';
或
$pattern = '[AB-]';
那麼減號就直接表示 '-' 字元,而不是範圍運算子了。
例:
$st = '99A88L773]';
$pattern = '[A-DK-N0-3]';
會找到以下的 match:
0 : A
1 : L
2 : 3
------------------------------------------------------------------------------------------------
[[ 字元類別 - 反相字元類別 (Negated Character Class) ]]
例如要表示「'A'或者'B'或者'C'」,就寫
$pattern = '[ABC]';
但如果要表示「'A'或者'B'或者'C'」的相反,即「不是'A'並且不是'B'並且不是'C'」
就可以寫
$pattern = '[^ABC]';
在字元類別內,如果第一個字元是 '^',就表示這是一個「反相字元類別」。
例:
$st = '11A22B33X44Y';
$pattern = '[^A-C0-9]';
會找到以下的 match:
0 : X
1 : Y
------------------------------------------------------------------------------------------------
[[ 字元類別 - 簡寫 ]]
Perl 提供了一些常用的字元類別簡寫,例如
$pattern = '[0-9]';
其實可以寫成
$pattern = '\d';
這就更簡潔了。以下列出這些簡寫:
\d 等於寫 [0-9]
\D 等於寫 [^0-9] ,即 \d 的相反
\w 等於寫 [0-9a-zA-Z_] ,即數字、英文大小寫字母和底線
\W 等於寫 [^0-9a-zA-Z_] ,即 \w 的相反
\s 等於寫 [\ \t\n\r\f] ,即白格字元 (Whitespace character),
包括space, tab, newline, carriage return, form feed
\S 等於寫 [^\ \t\n\r\f] ,即 \s 的相反
還有一個很特別的簡寫,就是點號 '.',
因為它比較複雜,所以稍後再介紹它。
例:
$st = '11A22b33_44=';
$pattern = '\D';
會找到以下的 match:
0 : A
1 : b
2 : _
3 : =
例:
$st = '11AA22A_33A044AX';
$pattern = 'A[^\dX-Z]';
會找到以下的 match:
0 : AA
1 : A_
------------------------------------------------------------------------------------------------
[[ Alternation ]]
如果想表示「'AB'字串或'KLM'字串或者'PQRST'」,可以寫
$pattern = 'AB|KLM|PQRST';
即用直線分隔它們。
Alternation 跟 Character Class 很像,
例如以下兩者是一樣的:
$pattern = '[ABC]';
$pattern = 'A|B|C';
不過 Character Class 不能表示超過一個字元的字串。
例:
$st = '11AB22KLMN33PQRS';
$pattern = 'AB|KLM|PQRST';
會找到以下的 match:
0 : AB
1 : KLM
如果寫:
$pattern = 'PQRST|KLM|AB';
那還是會先找到 'AB',效果跟 Character Class 一樣。
------------------------------------------------------------------------------------------------
[[群組 (Group)]]
如果想表示「'ABC'或者'ABKL'或者'ABPQR'」,除了可以寫:
$pattern = 'ABC|ABKL|ABPQR';
還可以這樣寫:
$pattern = 'AB(C|KL|PQR)';
即用括號來分開'AB'和接著的那三個 Alternatives,
好像數學上把 2*3 + 2*4 + 2*5 寫成 2 * (3+4+5)。
如果想連'AB'都要 match:
$pattern = 'AB|ABC|ABKL|ABPQR';
也可以把空字串都寫到括號內:
$pattern = 'AB(C|KL|PQR|)';
------------------------------------------------------------------------------------------------
[[群組 - 特殊變數 $1, $2, ...]]
括號還有一種特別的功能,就是把括號內 match 到的部份
放到特殊變數 $1, $2, ...。
例如您想檢查某字串是含有時間的模式,例如 HH:MM:SS,
(不檢查數值範圍),同時想取得時分秒各個部份,可以寫:
####
my ($time_string, $hour, $minute, $second);
$time_string = '12:34:56';
$pattern = '(\d\d):(\d\d):(\d\d)';
if ($time_string =~ /$pattern/) {
($hour, $minute, $second) = ($1, $2, $3);
print "hour=$hour,minute=$minute,second=$second";
} else {
print 'Invalid time format';
}
####
執行結果:
hour=12,minute=34,second=56
至於哪部份是 $1, $2, ...,就以開括號出現的先後順序來決定,
例如括著「時」那個開括號是第一個出現的,所以它就對應 $1。
(\d\d):(\d\d):(\d\d)
1______2______3
------------------------------------------------------------------------------------------------
[[群組 - 巢狀群組]]
括號內也可以有括號,成為巢狀群組,例如:
(\d\d):((\d\d):(\d\d))
1______23______4
####
my ($time_string, $hour, $minute, $second);
$time_string = '12:34:56';
$pattern = '(\d\d):((\d\d):(\d\d))';
if ($time_string =~ /$pattern/) {
print "\$1=$1\n\$2=$2\n\$3=$3\n\$4=$4";
} else {
print 'Invalid time format';
}
####
執行結果:
$1=12
$2=34:56
$3=34
$4=56
------------------------------------------------------------------------------------------------
[[群組 - 返回參照 (Backreferences) \1, \2, ...]]
跟 $1, $2, ... 有很密切關係的東西,就是 Backreferences,
即 \1, \2, ... 。
它們不同之處,在於 $1 只應該用在 regexp 「以外」,
而 \1 只應該用在 regexp 「以內」。
例:
####
$st = 'A_A';
$pattern = '([ABC])_\1';
if ($st =~ /$pattern/) {
print "\$1=$1";
}
####
執行結果:
$1=A
您可以嘗試把 $st 改成:
$st = 'B_B';
$st = 'C_C';
您會發現 \1 能夠取得之前所 match 的東西,
即應該放到 $1 的值。但要緊記,不應該在 regexp 以內寫 $1。
例:
####
$st = 'ALZ_A_L_Z';
$pattern = '([ABC])([KLM])([XYZ])_\1_\2_\3';
if ($st =~ /$pattern/) {
print "\$1=$1\n\$2=$2\n\$3=$3";
}
####
執行結果:
$1=A
$2=L
$3=Z
------------------------------------------------------------------------------------------------
[[ 群組 - $1, $2, ... 的開始和結束位置 ]]
Perl 5.6.0 提供了兩個陣列 @- 和 @+,
@- 儲存了 $1, $2, ... 的開始位置,@+ 就儲存結束位置。
例:
####
my ($time_string, $hour, $minute, $second);
$time_string = '12:34:56';
$pattern = '(\d\d):(\d\d):(\d\d)';
$time_string =~ /$pattern/;
foreach my $i (1 .. $#-) {
print "Match $i='${$i}' at position ($-[$i] , $+[$i])\n";
}
####
執行結果:
Match 1='12' at position (0 , 2)
Match 2='34' at position (3 , 5)
Match 3='56' at position (6 , 8)
------------------------------------------------------------------------------------------------
[[ 群組 - $` 、 $& 和 $' ]]
如果 regexp 沒有寫括號,就沒有 $1, $2, ... 可用了,
但您仍然可以用 $& 來取得 match,$` 則取得 match 前面所有東西,
$' 取得 match 後面所有東西。
####
$st = '11AA22';
$pattern = 'AA';
$st =~ /$pattern/;
print "\$`=$`\n\$&=$&\n\$'=$'";
####
執行結果:
$`=11
$&=AA
$'=22
如果用 substr 來表示,那麼:
$` 是 substr( $st, 0, $-[0] )
$& 是 substr( $st, $-[0], $+[0] - $-[0] )
$' 是 substr( $st, $+[0] )
------------------------------------------------------------------------------------------------
[[ 重覆 (Repetition) ]]
如果想表示「五個 'A' 字元」,可以寫
$pattern = 'AAAAA';
也可以用量化詞 (Quantifier) ,寫成
$pattern = 'A{5}';
這樣就更清楚了。而量化詞可以寫在字元、字元類別或群組的後面。
例:
$st = '11AAA22AAAAA';
$pattern = 'A{5}';
------------------------------------------------------------------------------------------------
[[重覆 - 最多和最少]]
量化詞可分成兩類:
1) 最多配對(貪心的) Maximal Match (Greedy)
2) 最少配對(不貪心的) Minimal Match (Non-greedy)
以下列出「最多配對」:
A? 表示 0 個或 1 個 'A' 字元(以最多為準)
A* 表示 0 個或以上的 'A' 字元(以最多為準)
A+ 表示 1 個或以上的 'A' 字元(以最多為準)
A{n} 表示 n 個 'A' 字元
A{n,m} 表示最少 n 個,最多 m 個 'A' 字元(以最多為準)
A{n,} 表示最少 n 個 'A' 字元(以最多為準)
以下列出「最少配對」:
A?? 表示 0 個或 1 個 'A' 字元(以最少為準)
A*? 表示 0 個或以上的 'A' 字元(以最少為準)
A+? 表示 1 個或以上的 'A' 字元(以最少為準)
A{n}? 表示 n 個 'A' 字元
(這個其實沒有所謂的「最多」還是「最少」之分,
定義這個符號只是為了保持符號一致性而己)
A{n, m}? 表示最少 n 個,最多 m 個 'A' 字元(以最少為準)
A{n,}? 表示最少 n 個 'A' 字元(以最少為準)
到底「最多配對」和「最少配對」有什麼分別呢?例如
$st = 'AAAAA';
$pattern1 = 'A{3,5}'; # 最多配對
$pattern2 = 'A{3,5}?'; # 最少配對
那麼 match 可能是:
1) 'AAA'(字元位置0至2)
2) 'AAAA'(字元位置0至3)
3) 'AAAAA'(字元位置0至4)
如果是「最多配對」,那麼 match 就是 'AAAAA',因為它是最多次數,
如果是「最少配對」,那麼 match 就是 'AAA',因為它是最少次數。
------------------------------------------------------------------------------------------------
[[ 特點字元 - 任何一個字元 ]]
如果要表示「任何一個字元」,可以用點號 '.'。
在預設的情況下,它會配到「任何一個字元,除了 "\n" 之外」:
$st = "123ABC_!@\n";
$pattern = '.';
@matches = $st =~ /$pattern/g;
如果要配到「任何一個字元,包括 "\n"」,
就到加入 s 這個「修飾詞」(Modifier):
@matches = $st =~ /$pattern/sg;
s 的意思是「把字串當作單行 (Single Line)」。
------------------------------------------------------------------------------------------------
[[ 字串的開頭 ]]
如果要表示「字串的開頭」,可以用 '^',例如:
$st = 'AA22';
$pattern = '^AA';
@matches = $st =~ /$pattern/g;
執行結果:
0 : AA
但 match 當中不會有 '^' 所配出來的字元,
因為 '^' 主要用來測試字串的特性(例如字串位置),
而不是配任何字元的。
為了方便理解,您可以把它「想像」成一個看得見的字元,
例如:
$st = '頭AA22尾'; ## $st = 'AA22'
$pattern = '頭AA'; ## $pattern = '^AA';
------------------------------------------------------------------------------------------------
[[ 字串的結尾 ]]
如果要表示「字串的結尾,或者在字串最尾的 \n 和前面字元的中間」,
可以用 '$',例如:
$st = "AA\nBB\n"; ## 用了雙引號
$pattern = '$';
@matches = $st =~ /$pattern/g;
結果有兩個 match ,但是'$' 與 '^' 一樣,是不會配到字元的。
您可以把它想像成:
$st = "頭AA\nBB尾\n尾"; ## $st = "AA\nBB\n";
$pattern = '尾'; ## $pattern = '$';
另外,當檢查一個字串是否符合某種格式時,
通常會 '^' 和 '$' 一起用,
例如檢查某字串是否符合「三位數字」的格式:
####
$st = "123";
$pattern = '^\d{3}$';
if ($st =~ /$pattern/) {
print "Valid format";
} else {
print "Invalid format";
}
####
------------------------------------------------------------------------------------------------
[[ 多行 (Multi-line) ]]
一個字串內可以含有 \n,例如:
$st = "AA\nAA\nAA\n";
$pattern = '^AA';
如果我們想 '^' 配到每一行的開頭,
可以用 m 修飾詞:
@matches = $st =~ /$pattern/mg;
執行結果:
0 : AA
1 : AA
2 : AA
想像成:
$st = "頭AA尾\n頭AA尾\n頭AA尾\n尾";
$pattern = '頭AA';
用了 m 修飾詞,也會令 '$' 配到每一行的結尾,
$st = "AA\nAA\nAA\n";
$pattern = 'AA$';
@matches = $st =~ /$pattern/mg;
想像成:
$st = "頭AA尾\n頭AA尾\n頭AA尾\n尾";
$pattern = 'AA尾';
如果用了 m 修飾詞,您仍然可以用:
\A 表示沒有 m 修飾詞的 '^' 意思
\Z 表示沒有 m 修飾詞的 '$' 意思
\z 代表字串的結尾(即 \Z ,但不包括最尾 \n 和前面字元的中間)
$st = "AA\nAA\nAA\n";
$pattern1 = '\AAA';
$pattern2 = 'AA\Z';
$pattern3 = 'AA\z';
總括來說,s 修飾詞會影響 '.' 的意思,
m 修飾詞會影響 '^' 和 '$' 的意思,
s 和 m 總共有四種可能:
沒有 s 和 m :預設模式 (Default Mode)
/s :單行模式 (Single Line Mode)
/m :多行模式 (Multi-Line Mode)
/sm :清楚多行模式 (Clean Multi-Line Mode)
看看大家能否解釋以下程式的執行結果:
####
$st = "A\nA\nA\n";
$pattern = '(^|$|.)';
@matches = $st =~ /$pattern/smg;
####
------------------------------------------------------------------------------------------------
[[ 字的邊界 Word Boundary ]]
字的邊界是指兩個連續字元的中間,
一個會配到 \w,而另一個會配到 \W。
(字串的開頭和結尾也視為配到 \W)
例如:
$st = 'ABC-=+';
$pattern = 'C\b';
@matches = $st =~ /$pattern/g;
因為左邊的 'C' 會配到 \w,而右邊的 '-' 會配到 \W,
所以 \b 會配到它們的中間。
\b 不代表某個字元,而是代表某個位置的特性。
您可以想像成:
$st = '界ABC界-=+';
$pattern = 'C界';
您可以用 \B 來表示 \b 的相反,即「不是邊界」,
例如:
$st = 'ABCDE';
$pattern = '\BC\B';
@matches = $st =~ /$pattern/g;
因為 'C' 本身配到 \w ,而它左邊的'B'和右邊的'D'也是配到 \w,
所以沒有邊界。
------------------------------------------------------------------------------------------------
[[ 不分大小寫 (Case-insensitive) ]]
如果加入了 i 修飾詞,就會做不分大小寫的配對,
例如:
$st = '11AA22BB';
$pattern = '[abAB]';
@matches = $st =~ /$pattern/g;
可以用 i 修飾詞寫成:
$st = '11AA22BB';
$pattern = '[AB]'; # 或者 '[ab]'
@matches = $st =~ /$pattern/ig;
因為不分大小寫,所以不必在 $pattern 把大小寫都列出來了。
------------------------------------------------------------------------------------------------
[[ x 修飾詞 ]]
如果用了 x 修飾詞,就會令 regexp 解譯器忽略 $pattern 內的白格,
(除了那些在被反斜號標示了的白格,及那些在字元類別內的白格)
還有在 $pattern 內的 '#' 也會有好像 Perl 一樣的單行註解作用,
(除了那些在被反斜號標示了的'#',及那些在字元類別內的'#')
它的用途是讓您可以把 regexp 寫成容易閱讀的方式,及加入註解。
例子:
不用 x 修飾詞的寫法:
$st = '11AA22 33\\\\44##';
$pattern = '(A| |\\\\|#)';
@matches = $st =~ /$pattern/g;
用了 x 修飾詞的寫法:
$st = '11AA22 33\\\\44##';
$pattern = q{
( # 左括號表示 Alternation 開始
A # 'A' 字元
| # 或者
[ ] # 用字元類別表示空格
| # 或者
\\\\ # 反斜號字元
|
[#] # 用字元類別表示'#'
) # 右括號表示完結
};
@matches = $st =~ /$pattern/xg;
------------------------------------------------------------------------------------------------
[[ 伸展模式 (Extended Patterns) ]]
除了傳統的 regexp 語法外,Perl 還定義了一些額外的語法,
但在其它工具未必有這些語法,而且有些語法可能還在實驗階段,
說不定將來會被刪除,詳情請參閱說明文件。
這些語法都是這樣子的:
(?char)
而 char 就表示伸展的種類。
------------------------------------------------------------------------------------------------
[[ 伸展模式 - 內嵌註解 (Embedding Comments) ]]
語法:
(?#text)
在 text 位置的東西會當作註解,
但要留意 text 內不可以含有右括號。
例:
$pattern = '(?# THIS IS A COMMENT)(A| |\\\\|#)';
------------------------------------------------------------------------------------------------
[[ 伸展模式 - 非捕捉群組 (Non-capturing groupings) ]]
語法:
(?:pattern)
之前提到括號有個特別的作用,就是會把配到的東西放進 $1, $2,...,
例如:
$time_string = '12:34:56';
$pattern = '(\d\d):(\d\d):(\d\d)';
if ($time_string =~ /$pattern/) {
($hour, $minute, $second) = ($1, $2, $3);
print "hour=$hour,minute=$minute,second=$second";
}
但如果您不想放的話,可以用 (?:pattern):
$time_string = '12:34:56';
$pattern = '(\d\d):(?:\d\d):(\d\d)';
if ($time_string =~ /$pattern/) {
($hour, $minute, $second) = ($1, $2, $3);
print "hour=$hour,minute=$minute,second=$second";
}
因為「分」那部份使用了(?:pattern) ,所以就不會放到本來的 $2,
這就輪到「秒」的部份放到 $2了。
------------------------------------------------------------------------------------------------
[[ 伸展模式 - 內嵌修飾詞 (Embedded Modifiers) ]]
語法:
(?imsx-imsx:pattern)
之前提到可以用 i 修飾詞來做不分大小寫的配對,例如:
$st = '11AB22Ab33aB44ab';
$pattern = 'AB';
@matches = $st =~ /$pattern/ig;
這樣整個 $pattern 都是不分大小寫了,
如果想某部份分辨大小寫,某部份不分辨,
可以用內嵌的寫法。
$st = '11AB22Ab33aB44ab';
$pattern = '(?i:A)(?-i:B)';
@matches = $st =~ /$pattern/g;
表示 A 不分大小寫,B 就分辨大小寫。
如果修飾詞前面是減號,表示關閉該修飾詞,
例如 (?s-i),表示啟動 s 修飾詞,關閉 i 修飾詞。
其實內嵌修飾詞的語法,是在非捕捉群組內加入修飾詞而己。
------------------------------------------------------------------------------------------------
[[ 伸展模式 - 往前看(Look-ahead)和往後看(Look-behind) ]]
語法:
往前看(正)(Positive Look-ahead) (?=pattern)
往後看(正)(Positive Look-behind) (?<=pattern)
往前看(負)(Negative Look-ahead) (?!pattern)
往後看(負)(Negative Look-behind) (?
舉例來說:
$st = 'ABCDEFGH';
$pattern = '\w{2}';
@matches = $st =~ /$pattern/g;
執行結果:
0 : AB
1 : CD
2 : EF
3 : GH
您可以想像它是由左往右走,當配到一個match,
下一次就由match之後開始走:
ABCDEFGH
由A的位置開始走,配到 AB,剩下 CDEFGH ('AB')
由C的位置開始走,配到 CD,剩下 EFGH ('AB', 'CD')
由E的位置開始走,配到 EF,剩下 GH ('AB', 'CD', 'EF')
由G的位置開始走,配到 GH,剩下 ('AB', 'CD', 'EF', 'GH')
如果想在走路的時候,還要同時往前面看,
可以用「往前看」或「往後看」。
例如仍然要找出 \w{2},但同時要檢查它的前面(即右面)必須是\w{2},
可以寫:
$pattern = '\w{2}(?=\w{2})';
執行結果:
0 : AB
1 : CD
2 : EF
這裡的 (?=\w{2}) 用了「往前看(正)」,
這部份是不會配到任何字元,只是用來檢查字串的特性:
ABCDEFGH
由A的位置開始,配到AB,往前看到CD,剩下 CDEFGH ('AB')
由C的位置開始,配到CD,往前看到EF,剩下 EFGH ('AB', 'CD')
由E的位置開始,配到EF,往前看到GH,剩下 GH ('AB', 'CD', 'EF')
由G的位置開始,雖然GH 可以配到\w{2} ,但往前看不到 \w{2},所以沒有match
完結
同理,如果改用「往後看」:
$pattern = '(?<=\w{2})\w{2}';
執行結果:
0 : CD
1 : EF
2 : GH
ABCDEFGH
由A的位置開始,直至走到C的位置時,
才能夠往後看到 AB,配到CD,剩下 EFGH ('CD')
由E的位置開始,往後看到CD,配到EF,剩下 GH ('CD', 'EF')
由G的位置開始,往後看到EF,配到GH,剩下 ('CD', 'EF', 'GH')
完結
Negative Look-ahead 是 Positve Look-ahead的相反,
例如仍然要找出 \w{2},但同時要檢查它的前面必須「不是」\w{2},
可以寫:
$st = 'ABCDEFGH';
$pattern = '\w{2}(?!\w{2})';
@matches = $st =~ /$pattern/g;
執行結果:
0 : FG
因為 'FG'的右面只有 'H' ,即 \w ,而不是 \w{2},
所以配到。
原文連結:
http://ck101.com/forums/viewthread.php?tid=1270923&extra=page%3D1%26amp%3Bfilter%3Dtype%26amp%3Btypeid%3D29
0 意見:
張貼留言張貼留言