perlrequick - Perl 正規表示式快速入門
此頁面涵蓋在 Perl 中理解、建立和使用正規表示式(「regexes」)的基本知識。
此頁面假設您已經知道某些事情,例如「樣式」是什麼,以及使用它們的基本語法。如果您不知道,請參閱 perlretut。
最簡單的正規表示式只是一個字詞,或者更廣泛地說,是一個字元串。由一個字詞組成的正規表示式會配對任何包含該字詞的字串
"Hello World" =~ /World/; # matches
在此陳述中,World
是正規表示式,而包住 /World/
的 //
告訴 Perl 在字串中搜尋配對。運算子 =~
將字串與正規表示式配對關聯起來,如果正規表示式配對,則產生真值,如果正規表示式未配對,則產生假值。在我們的案例中,World
配對 "Hello World"
中的第二個字詞,因此這個表達式為真。這個概念有許多變化。
此類表達式在條件式中很有用
print "It matches\n" if "Hello World" =~ /World/;
使用 !~
運算子可以反轉比對的意義
print "It doesn't match\n" if "Hello World" !~ /World/;
正規表示式中的字串文字可以用變數取代
$greeting = "World";
print "It matches\n" if "Hello World" =~ /$greeting/;
如果要與 $_
比對,可以省略 $_ =~
部分
$_ = "Hello World";
print "It matches\n" if /World/;
最後,比對的預設分隔符號 //
可以改成任意分隔符號,只要在前面加上 'm'
即可
"Hello World" =~ m!World!; # matches, delimited by '!'
"Hello World" =~ m{World}; # matches, note the matching '{}'
"/usr/bin/perl" =~ m"/perl"; # matches after '/usr/bin',
# '/' becomes an ordinary char
正規表示式必須與字串的一部分完全相符,陳述式才會為真
"Hello World" =~ /world/; # doesn't match, case sensitive
"Hello World" =~ /o W/; # matches, ' ' is an ordinary char
"Hello World" =~ /World /; # doesn't match, no ' ' at end
Perl 會永遠在字串中最早可能的地方進行比對
"Hello World" =~ /o/; # matches 'o' in 'Hello'
"That hat is red" =~ /hat/; # matches 'hat' in 'That'
並非所有字元都能在比對中「照樣」使用。有些字元,稱為後設字元,被視為特殊字元,並保留在正規表示式符號中使用。後設字元包括
{}[]()^$.|*+?\
後設字元可以在前面加上反斜線後照樣比對
"2+2=4" =~ /2+2/; # doesn't match, + is a metacharacter
"2+2=4" =~ /2\+2/; # matches, \+ is treated like an ordinary +
'C:\WIN32' =~ /C:\\WIN/; # matches
"/usr/bin/perl" =~ /\/usr\/bin\/perl/; # matches
在最後一個正規表示式中,正斜線 '/'
也加上反斜線,因為它用於分隔正規表示式。
大部分後設字元並非總是特殊字元,而且其他字元(例如分隔模式的字元)會在各種情況下變成特殊字元。這可能會令人困惑,並導致意外的結果。use re 'strict'
可以通知您潛在的陷阱。
不可列印的 ASCII 字元用跳脫序列表示。常見範例包括表示 tab 的 \t
、表示換行的 \n
以及表示回車的 \r
。任意位元組用八進位跳脫序列表示,例如 \033
,或十六進位跳脫序列表示,例如 \x1B
"1000\t2000" =~ m(0\t2) # matches
"cat" =~ /\143\x61\x74/ # matches in ASCII, but
# a weird way to spell cat
正規表示式大部分視為雙引號字串,因此變數替換有效
$foo = 'house';
'cathouse' =~ /cat$foo/; # matches
'housecat' =~ /${foo}cat/; # matches
對於以上所有正規表示式,如果正規表示式在字串中的任何地方比對成功,則視為比對成功。若要指定何處應該比對,我們會使用錨定後設字元 ^
和 $
。錨定字元 ^
表示在字串開頭比對,而錨定字元 $
表示在字串結尾或字串結尾前的換行比對。一些範例
"housekeeper" =~ /keeper/; # matches
"housekeeper" =~ /^keeper/; # doesn't match
"housekeeper" =~ /keeper$/; # matches
"housekeeper\n" =~ /keeper$/; # matches
"housekeeper" =~ /^housekeeper$/; # matches
字元類別允許一組可能的字元,而不仅仅是單一字元,在正規表示式的特定點進行比對。有許多不同類型的字元類別,但通常人們使用這個術語時,指的是本節中描述的類型,技術上稱為「方括號字元類別」,因為它們用方括號 [...]
表示,其中包含可能比對的字元組。但我們會省略「方括號」以符合一般用法。以下是(方括號)字元類別的一些範例
/cat/; # matches 'cat'
/[bcr]at/; # matches 'bat', 'cat', or 'rat'
"abc" =~ /[cab]/; # matches 'a'
在最後一個陳述式中,即使 'c'
是類別中的第一個字元,正規表示式最早可以比對的點是 'a'
。
/[yY][eE][sS]/; # match 'yes' in a case-insensitive way
# 'yes', 'Yes', 'YES', etc.
/yes/i; # also match 'yes' in a case-insensitive way
最後一個範例顯示一個使用 'i'
修飾詞 的比對,這會讓比對不分大小寫。
字元類別也有普通字元和特殊字元,但字元類別內的普通字元和特殊字元集合與字元類別外的不同。字元類別的特殊字元為 -]\^$
,並使用跳脫字元進行比對
/[\]c]def/; # matches ']def' or 'cdef'
$x = 'bcr';
/[$x]at/; # matches 'bat, 'cat', or 'rat'
/[\$x]at/; # matches '$at' or 'xat'
/[\\$x]at/; # matches '\at', 'bat, 'cat', or 'rat'
特殊字元 '-'
在字元類別中作為範圍運算子,因此笨重的 [0123456789]
和 [abc...xyz]
變成輕巧的 [0-9]
和 [a-z]
/item[0-9]/; # matches 'item0' or ... or 'item9'
/[0-9a-fA-F]/; # matches a hexadecimal digit
如果 '-'
是字元類別中的第一個或最後一個字元,則會當作普通字元處理。
字元類別第一個位置的特殊字元 ^
表示否定字元類別,它會比對括號中的任何字元。[...]
和 [^...]
都必須比對到一個字元,否則比對會失敗。然後
/[^a]at/; # doesn't match 'aat' or 'at', but matches
# all other 'bat', 'cat, '0at', '%at', etc.
/[^0-9]/; # matches a non-numeric character
/[a^]at/; # matches 'aat' or '^at'; here '^' is ordinary
Perl 有幾個常見字元類別的縮寫。(這些定義是 Perl 在 ASCII 安全模式中使用 /a
修飾詞時使用的。否則它們還可以比對更多非 ASCII Unicode 字元。請參閱 "perlrecharclass 中的「跳脫字元序列」 以取得詳細資訊。)
\d 是數字,表示
[0-9]
\s 是空白字元,表示
[\ \t\r\n\f]
\w 是字元(字母數字或 _),表示
[0-9a-zA-Z_]
\D 是否定的 \d;它表示任何非數字字元
[^0-9]
\S 是否定的 \s;它表示任何非空白字元
[^\s]
\W 是否定的 \w;它表示任何非字元字元
[^\w]
句點 '.' 比對任何非 "\n" 字元
縮寫 \d\s\w\D\S\W
可以用於字元類別內外。以下是一些使用範例
/\d\d:\d\d:\d\d/; # matches a hh:mm:ss time format
/[\d\s]/; # matches any digit or whitespace character
/\w\W\w/; # matches a word char, followed by a
# non-word char, followed by a word char
/..rt/; # matches any two chars, followed by 'rt'
/end\./; # matches 'end.'
/end[.]/; # same thing, matches 'end.'
字詞錨定 \b
比對字元和非字元之間的界線 \w\W
或 \W\w
$x = "Housecat catenates house and cat";
$x =~ /\bcat/; # matches cat in 'catenates'
$x =~ /cat\b/; # matches cat in 'housecat'
$x =~ /\bcat\b/; # matches 'cat' at end of string
在最後一個範例中,字串的結尾被視為字詞界線。
對於自然語言處理(例如,使字詞包含撇號),請改用 \b{wb}
"don't" =~ / .+? \b{wb} /x; # matches the whole string
我們可以使用交替元字元 '|'
來比對不同的字串。為了比對 dog
或 cat
,我們形成正規表示式 dog|cat
。和之前一樣,Perl 會嘗試在字串中最早可能的位置比對正規表示式。在每個字元位置,Perl 會先嘗試比對第一個選項 dog
。如果 dog
沒有比對到,Perl 接著會嘗試下一個選項 cat
。如果 cat
沒有比對到,則比對失敗,Perl 會移到字串中的下一個位置。一些範例
"cats and dogs" =~ /cat|dog|bird/; # matches "cat"
"cats and dogs" =~ /dog|cat|bird/; # matches "cat"
即使 dog
是第二個正規表示式中的第一個選項,cat
仍能在字串中較早比對到。
"cats" =~ /c|ca|cat|cats/; # matches "c"
"cats" =~ /cats|cat|ca|c/; # matches "cats"
在給定的字元位置,允許正規表示式比對成功的第一個選項將會是比對到的那個。這裡,所有選項都在第一個字串位置比對到,所以第一個比對到。
群組元字元 ()
允許將正規表示式的一部分視為單一單位。正規表示式的一部分透過將它們括在括號中來群組。正規表示式 house(cat|keeper)
表示比對 house
後面接著 cat
或 keeper
。更多範例如下
/(a|b)b/; # matches 'ab' or 'bb'
/(^a|b)c/; # matches 'ac' at start of string or 'bc' anywhere
/house(cat|)/; # matches either 'housecat' or 'house'
/house(cat(s|)|)/; # matches either 'housecats' or 'housecat' or
# 'house'. Note groups can be nested.
"20" =~ /(19|20|)\d\d/; # matches the null alternative '()\d\d',
# because '20\d\d' can't match
群組元字元 ()
也允許擷取比對到的字串部分。對於每個群組,比對到的部分會放入特殊變數 $1
、$2
等。它們可以用作一般變數
# extract hours, minutes, seconds
$time =~ /(\d\d):(\d\d):(\d\d)/; # match hh:mm:ss format
$hours = $1;
$minutes = $2;
$seconds = $3;
在清單內容中,包含群組的比對 /regex/
會傳回比對到的值清單 ($1,$2,...)
。所以我們可以將它改寫成
($hours, $minutes, $second) = ($time =~ /(\d\d):(\d\d):(\d\d)/);
如果正規表示式中的群組是巢狀的,$1
會取得最左邊開啟括號的群組,$2
會取得下一個開啟括號,依此類推。例如,這裡是一個複雜的正規表示式,以及它下方標示的比對變數
/(ab(cd|ef)((gi)|j))/;
1 2 34
與匹配變數 $1
、$2
等相關聯的是 反向參考 \g1
、\g2
等。反向參考是可以在正規表示式內部使用的匹配變數
/(\w\w\w)\s\g1/; # find sequences like 'the the' in string
$1
、$2
等只能在正規表示式外部使用,而 \g1
、\g2
等只能在正規表示式內部使用。
量詞元字元 ?
、*
、+
和 {}
讓我們可以確定我們視為匹配的正規表示式一部分的重複次數。量詞放在我們想要指定的字元、字元類別或分組之後。它們具有下列意義
a?
= 匹配 'a' 1 次或 0 次
a*
= 匹配 'a' 0 次或多次,也就是說,任意次數
a+
= 匹配 'a' 1 次或多次,也就是說,至少一次
a{n,m}
= 匹配至少 n
次,但不多於 m
次。
a{n,}
= 匹配至少 n
次或更多次
a{,n}
= 匹配 n
次或更少次
a{n}
= 匹配正好 n
次
以下是一些範例
/[a-z]+\s+\d*/; # match a lowercase word, at least some space, and
# any number of digits
/(\w+)\s+\g1/; # match doubled words of arbitrary length
$year =~ /^\d{2,4}$/; # make sure year is at least 2 but not more
# than 4 digits
$year =~ /^\d{ 4 }$|^\d{2}$/; # better match; throw out 3 digit dates
這些量詞會嘗試匹配字串的盡可能多部分,同時仍然允許正規表示式匹配。因此我們有
$x = 'the cat in the hat';
$x =~ /^(.*)(at)(.*)$/; # matches,
# $1 = 'the cat in the h'
# $2 = 'at'
# $3 = '' (0 matches)
第一個量詞 .*
擷取字串的盡可能多部分,同時仍然讓正規表示式匹配。第二個量詞 .*
沒有字串可以擷取,因此它匹配 0 次。
有一些關於匹配運算子的其他事項您可能想要知道。全域修改器 /g
允許匹配運算子在字串中盡可能多次匹配。在純量內容中,對字串的連續匹配會讓 /g
從匹配跳到匹配,同時追蹤字串中的位置。您可以使用 pos()
函數取得或設定位置。例如,
$x = "cat dog house"; # 3 words
while ($x =~ /(\w+)/g) {
print "Word is $1, ends at position ", pos $x, "\n";
}
列印
Word is cat, ends at position 3
Word is dog, ends at position 7
Word is house, ends at position 13
失敗的匹配或變更目標字串會重設位置。如果您不希望在失敗匹配後重設位置,請新增 /c
,例如 /regex/gc
。
在清單內容中,/g
會傳回已匹配分組的清單,或者如果沒有分組,則傳回與整個正規表示式匹配的清單。因此
@words = ($x =~ /(\w+)/g); # matches,
# $word[0] = 'cat'
# $word[1] = 'dog'
# $word[2] = 'house'
搜尋和取代使用 s/regex/replacement/modifiers
執行。replacement
是 Perl 雙引號字串,它會在字串中取代與 regex
匹配的任何內容。運算子 =~
也用於在此將字串與 s///
關聯起來。如果與 $_
匹配,則可以省略 $_ =~
。如果有匹配,s///
會傳回已進行的取代次數;否則,它會傳回 false。以下是一些範例
$x = "Time to feed the cat!";
$x =~ s/cat/hacker/; # $x contains "Time to feed the hacker!"
$y = "'quoted words'";
$y =~ s/^'(.*)'$/$1/; # strip single quotes,
# $y contains "quoted words"
使用 s///
算子,匹配的變數 $1
、$2
等可立即用於替換表達式中。使用全域修飾詞 s///g
會搜尋並替換字串中所有出現的正規表示式
$x = "I batted 4 for 4";
$x =~ s/4/four/; # $x contains "I batted four for 4"
$x = "I batted 4 for 4";
$x =~ s/4/four/g; # $x contains "I batted four for four"
非破壞性修飾詞 s///r
會導致替換結果傳回,而不是修改 $_
(或替換繫結至 =~
的任何變數)
$x = "I like dogs.";
$y = $x =~ s/dogs/cats/r;
print "$x $y\n"; # prints "I like dogs. I like cats."
$x = "Cats are great.";
print $x =~ s/Cats/Dogs/r =~ s/Dogs/Frogs/r =~
s/Frogs/Hedgehogs/r, "\n";
# prints "Hedgehogs are great."
@foo = map { s/[a-z]/X/r } qw(a b c 1 2 3);
# @foo is now qw(X X X 1 2 3)
評估修飾詞 s///e
會將 eval{...}
包在替換字串周圍,並將評估結果替換為匹配的子字串。一些範例
# reverse all the words in a string
$x = "the cat in the hat";
$x =~ s/(\w+)/reverse $1/ge; # $x contains "eht tac ni eht tah"
# convert percentage to decimal
$x = "A 39% hit rate";
$x =~ s!(\d+)%!$1/100!e; # $x contains "A 0.39 hit rate"
最後一個範例顯示 s///
可以使用其他分隔符,例如 s!!!
和 s{}{}
,甚至是 s{}//
。如果使用單引號 s'''
,則正規表示式和替換會被視為單引號字串。
split /regex/, string
會將 string
分割成子字串清單,並傳回該清單。正規表示式會決定 string
根據什麼字元序列分割。例如,要將字串分割成字詞,請使用
$x = "Calvin and Hobbes";
@word = split /\s+/, $x; # $word[0] = 'Calvin'
# $word[1] = 'and'
# $word[2] = 'Hobbes'
要萃取逗號分隔的數字清單,請使用
$x = "1.618,2.718, 3.142";
@const = split /,\s*/, $x; # $const[0] = '1.618'
# $const[1] = '2.718'
# $const[2] = '3.142'
如果使用空正規表示式 //
,則會將字串分割成個別字元。如果正規表示式有群組,則產生的清單也會包含群組中匹配的子字串
$x = "/usr/bin";
@parts = split m!(/)!, $x; # $parts[0] = ''
# $parts[1] = '/'
# $parts[2] = 'usr'
# $parts[3] = '/'
# $parts[4] = 'bin'
由於 $x 的第一個字元與正規表示式相符,因此 split
會在清單前面加上一個空的初始元素。
use re 'strict'
v5.22 的新功能,在編譯正規表示式模式時,會套用比其他情況更嚴格的規則。它可以找到一些合法的東西,但可能不是您想要的。
請參閱 'strict' in re。
無。
這只是一個快速入門指南。有關正規表示式的更深入教學,請參閱 perlretut,有關參考頁面,請參閱 perlre。
版權所有 (c) 2000 Mark Kvale 保留所有權利。
本文件可以在與 Perl 相同的條款下散布。
作者要感謝 Mark-Jason Dominus、Tom Christiansen、Ilya Zakharevich、Brad Hughes 和 Mike Giroux 提供所有有用的意見。