第六章 正則表達(dá)式

Perl大部分的文本處理能力來(lái)自它所使用的正則表達(dá)式悠汽。
所謂正則表達(dá)式就是一個(gè)模式 渊啰,這個(gè)模式描述了一段文本所具有的特征蜡豹。正則表達(dá)式引擎就應(yīng)用這些模式來(lái)匹配和替換文本销凑。

Perl中相關(guān)的正則表達(dá)式文檔有:

perldoc perlretut 入門(mén)教程
perldoc perlreref 參考指南
perldoc perlre 有關(guān)正則的完整文檔

字面量

一個(gè)正則表達(dá)式可以簡(jiǎn)單到就是一些字符:

my $name = 'Chatfield';
say 'Found a hat!' if $name =~ /hat/;

匹配操作符(m//, 簡(jiǎn)寫(xiě)為 //)標(biāo)識(shí)著這是一個(gè)正則表達(dá)式。
這個(gè)正則表達(dá)式表示了這樣的一個(gè)模式特征:字符h 后面跟著一個(gè)字符a渺绒,再后面是個(gè)字符t贺喝。

正則表達(dá)式的綁定操作符(=~ )是一個(gè)中綴操作符,前面是要測(cè)試的字符串宗兼,后面是正則表達(dá)式(模式)躏鱼。
在標(biāo)量語(yǔ)境中,若匹配則返回真值殷绍,不匹配就返回假染苛。綁定操作符的否定形式(!~)求值規(guī)則相反:匹配返回假值,不匹配返回真值主到。

替換操作符(s///)茶行,某種意義上來(lái)說(shuō)是個(gè)環(huán)綴操作符,它有2個(gè)操作數(shù)登钥,第一個(gè)操作數(shù)是正則表達(dá)式畔师,第二個(gè)操作數(shù)是將匹配的部分替換成什么。使用綁定操作符來(lái)綁定操作對(duì)象牧牢。

my $status = 'I feel ill.';
$status =~ s/ill/well/;
say $status;

#I feel well.

qr//操作符和正則表達(dá)式組合

qr//操作符會(huì)創(chuàng)建一個(gè)正則表達(dá)式看锉,這樣就能在使用正則表達(dá)式的地方直接使用:

my $hat = qr/hat/;
say 'Found a hat!' if $name =~ /$hat/;


#組合起來(lái)使用:
my $hat = qr/hat/;
my $field = qr/field/;
say 'Found a hat in a field!'
if $name =~ /$hat$field/;
like( $name, qr/$hat$field/, 'Found a hat in a field!' );
#Test::More 的like()函數(shù)姿锭,第一個(gè)參數(shù)是要測(cè)試的目標(biāo),第二個(gè)參數(shù)是正則表達(dá)式模式伯铣。

量詞

使用量詞可以使正則表達(dá)式威力大增呻此。量詞用來(lái)表示字符出現(xiàn)的頻率。

?表示0次或1次:

my $cat_or_ct = qr/ca?t/;    
#ct匹配腔寡,cat匹配焚鲜,caat不匹配

like( 'cat', $cat_or_ct, "'cat' matches /ca?t/" );
like( 'ct', $cat_or_ct, "'ct' matches /ca?t/" );

+表示一次或多次(也就是至少一次):

my $some_a = qr/ca+t/;

like( 'cat', $some_a, "'cat' matches /ca+t/" );
like( 'caat', $some_a, "'caat' matches/" );
like( 'caaat', $some_a, "'caaat' matches" );
like( 'caaaat', $some_a, "'caaaat' matches" );
unlike( 'ct', $some_a, "'ct' does not match" );

*表示任意次,0次蹬蚁、1次恃泪、多次均匹配:

my $any_a = qr/ca*t/;

like( 'cat', $any_a, "'cat' matches /ca*t/" );
like( 'caat', $any_a, "'caat' matches" );
like( 'caaat', $any_a, "'caaat' matches" );
like( 'caaaat', $any_a, "'caaaat' matches" );
like( 'ct', $any_a, "'ct' matches" );

#量詞*的使用需要留心,容易出錯(cuò)犀斋。

指定明確次數(shù)n次贝乎,{n}

# equivalent to qr/cat/;
my $only_one_a = qr/ca{1}t/;

like( 'cat', $only_one_a, "'cat' matches /ca{1}t/" );

{n,}至少n次

# equivalent to qr/ca+t/;
my $some_a = qr/ca{1,}t/;

like( 'cat', $some_a, "'cat' matches /ca{1,}t/" );
like( 'caat', $some_a, "'caat' matches" );
like( 'caaat', $some_a, "'caaat' matches" );
like( 'caaaat', $some_a, "'caaaat' matches" );

{n,m}不少于n次,不大于m次

my $few_a = qr/ca{1,3}t/;

like( 'cat', $few_a, "'cat' matches /ca{1,3}t/" );
like( 'caat', $few_a, "'caat' matches" );
like( 'caaat', $few_a, "'caaat' matches" );
unlike( 'caaaat', $few_a, "'caaaat' doesn't match" );

貪婪

量詞+ 和*具有貪婪的特性叽粹,默認(rèn)它們會(huì)盡可能多去匹配览效。

# a poor regex
my $hot_meal = qr/hot.*meal/;

say 'Found a hot meal!' if 'I have a hot meal' =~ $hot_meal;
say 'Found a hot meal!' if 'one-shot, piecemeal work!' =~ $hot_meal;

使用?來(lái)轉(zhuǎn)化為非貪婪的,非貪婪量詞會(huì)盡量短的匹配虫几。

my $minimal_greedy = qr/hot.*?meal/;

錨位

錨位就是做位置上的控制锤灿。
字符串開(kāi)始(\A)

# also matches "lammed", "lawmaker", and "layman"
my $seven_down = qr/\Al${letters_only}{2}m/;

end of line string anchor (\z)
# also matches "loom", but an obvious improvement
my $seven_down = qr/\Al${letters_only}{2}m\z/;

你還可能見(jiàn)過(guò)用^ 和 $用來(lái)表示字符串開(kāi)始和結(jié)束位置。^其實(shí)是匹配換行符后面辆脸,同樣的$匹配的是換行符前面但校。\A 和\z更具體,建議使用這種方式啡氢。

字符邊界(\b)状囱,匹配的是字符(\w)和非字符(\W)之間的那個(gè)位置。邊界不是一個(gè)字符倘是,它0寬度亭枷,不可見(jiàn)。

my $seven_down = qr/\bl${letters_only}{2}m\b/;

元字符

正則表達(dá)式中搀崭,元字符代表的不是字面本身的意思叨粘,而是解釋出來(lái)的意思。我們已經(jīng)見(jiàn)過(guò)元字符了瘤睹,比如斜杠(\)和量詞(?)升敲。
一些元字符:

.  代表?yè)Q行符之外的一切東西
\w  代表所有數(shù)字,字符和下劃線轰传。
\d  代表所有數(shù)字
\s  代表空白:空格驴党,制表符,回車(chē)绸吸,換頁(yè)鼻弧,換行符

一般使用大寫(xiě)來(lái)表示相反的意思。

\ W匹配任何字符锦茁,除了數(shù)字攘轩,字符和下劃線
\ D匹配一個(gè)非數(shù)字字符
\ S匹配任何東西,但空白除外
\ B匹配任何位置除了字符邊界

字符集

你可以使用方括號(hào)來(lái)組織需要的候選項(xiàng):

my $ascii_vowels = qr/[aeiou]/;
my $maybe_cat = qr/c${ascii_vowels}t/;

方括號(hào)中還允許使用連字符(-)來(lái)包含連續(xù)的范圍:

my $ascii_letters_only = qr/[a-zA-Z]/;

如果要將連字符作為候選的成員码俩,可以將它放在最前面或最后面或進(jìn)行轉(zhuǎn)義:

my $interesting_punctuation = qr/[-!?]/;
my $line_characters = qr/[|=\-_]/;

在最前面放置^表示取反:

my $not_an_ascii_vowel = qr/[^aeiou]/;

#如果想要把它是作為候選的成員度帮,那就放在其他位置,或者直接轉(zhuǎn)義它稿存。

捕獲

正則表達(dá)式允許你使用括號(hào)來(lái)捕獲匹配的部分笨篷,以備過(guò)后使用。

#假設(shè)電話號(hào)碼為:(202)456-1111
my $area_code = qr/\(\d{3}\)/;
my $local_number = qr/\d{3}-?\d{4}/;
my $phone_number = qr/$area_code\s?$local_number/;

#注意第一行的括號(hào)需要轉(zhuǎn)義瓣履,因?yàn)樾枰瓨拥姆诺礁鼜?fù)雜的正則表達(dá)式中率翅。

以左括號(hào)的次序,依次將匹配的結(jié)果存入變量$1袖迎,$2冕臭,$3....

#捕獲
if ($contact_info =~ /($phone_number)/)
{
say "Found a number $1";
}

在列表語(yǔ)境中,返回的是捕獲結(jié)果燕锥。

給捕獲命名

我們可以給要捕獲的組增加名字來(lái)更好的識(shí)別它:

if ($contact_info =~ /(?<phone>$phone_number)/)
{
say "Found a number $+{phone}";
}

命名捕獲的語(yǔ)法:

(?<capture name> ... )

匹配成功時(shí)辜贵,以名字為鍵,匹配的部分為值存入到“%+”這個(gè)哈希中归形。

分組

my $pork = qr/pork/;
my $beans = qr/beans/;

like( 'pork and beans', qr/\A$pork?.*?$beans/, 'maybe pork, definitely beans' );

如果你自己手動(dòng)展開(kāi)這個(gè)正則表達(dá)式托慨,結(jié)果可能不是你期望的:

my $pork_and_beans = qr/\Apork?.*beans/;

like( 'pork and beans', qr/$pork_and_beans/, 'maybe pork, definitely beans' );
like( 'por and beans', qr/$pork_and_beans/, 'wait... no phylloquinone here!' );
#均會(huì)匹配

來(lái)我們把模式寫(xiě)得更精確些:

my $pork = qr/pork/;
my $and = qr/and/;
my $beans = qr/beans/;

like( 'pork and beans', qr/\A$pork? $and? $beans/, 'maybe pork, maybe and, definitely beans' );

可選操作符(|):

my $rice = qr/rice/;
my $beans = qr/beans/;

like( 'rice', qr/$rice|$beans/, 'Found rice' );
like( 'beans', qr/$rice|$beans/, 'Found beans' );

這樣寫(xiě)容易產(chǎn)生疑惑:

like( 'rice', qr/rice|beans/, 'Found rice' );
like( 'beans', qr/rice|beans/, 'Found beans' );
unlike( 'ricb', qr/rice|beans/, 'Found hybrid' );

但是實(shí)際上由于| 的操作符優(yōu)先級(jí)很低,意思是一樣的暇榴,不過(guò)還是使用變量比較清楚厚棵。

括號(hào)默認(rèn)會(huì)啟用捕獲,但是可以指定為不捕獲跺撼。

my $starches = qr/(?:pasta|potatoes|rice)/;
#不捕獲窟感,括號(hào)只是用來(lái)分組。

其他轉(zhuǎn)義序列

對(duì)于元字符歉井,要匹配它們的字面意思就需要轉(zhuǎn)義柿祈。

匹配左括號(hào) \( 
匹配字符點(diǎn) \. 
這些字符也要轉(zhuǎn)義才能表示它們的字面意思 |,$哩至,+躏嚎,?,* 

還有一個(gè)方法就是使用元字符禁用字符(\Q \E)來(lái)禁用元字符特性菩貌。當(dāng)模式來(lái)源于外部(你無(wú)法控制)時(shí)卢佣,這個(gè)特性非常有用:

my ($text, $literal_text) = @_;
return $text =~ /\Q$literal_text\E/;

#此時(shí)$literal_text里面的字符將沒(méi)有元字符特性,任何字符都是字面意義箭阶。
#比如是字符串 .*A就等同\.\*A
#表示一個(gè)點(diǎn)號(hào)一個(gè)*號(hào)一個(gè)A虚茶。

處理用戶輸入的模式時(shí)要小心戈鲁,一個(gè)惡意的正則表達(dá)式可能需要花費(fèi)超常時(shí)間來(lái)匹配,從而導(dǎo)致拒絕服務(wù)攻擊嘹叫。

斷言

正則表達(dá)式中的錨位如\A \b \B \Z就是斷言婆殿,斷言的作用是要求字符串符合一定的條件,本身并不匹配任何字符(所以又稱零寬斷言)罩扇。無(wú)論字符串的內(nèi)容是什么婆芦,正則表達(dá)式qr/\A/總是會(huì)匹配成功。

這是一個(gè)很重要的特性:零寬斷言不會(huì)消費(fèi)字符串喂饥,它只是一種要求模式消约。比如你想要找到單詞cat,就可以用邊界斷言(錨位):

my $just_a_cat = qr/cat\b/;

你也可以要求cat后面不能是字符串a(chǎn)strophe员帮,這時(shí)就可以使用零寬否定前向斷言(?!...)語(yǔ)法:

my $safe_feline = qr/cat(?!astrophe)/;

對(duì)應(yīng)的還有零寬肯定前向斷言(?=...):

my $disastrous_feline = qr/cat(?=astrophe)/;
#cat后面必須是字符串a(chǎn)strophe才能匹配或粮。

還有向后的零寬斷言,零寬否定后向斷言(?<!...):

my $middle_cat = qr/(?<!\A)cat/;
#不能一開(kāi)始就是cat

零寬肯定后向斷言(?<=...):

my $space_cat = qr/(?<=\s)cat/;
#cat前面必須是空白字符

Perl 正則表達(dá)式還有個(gè)功能:保持?jǐn)嘌裕╘K)集侯,它的作用是用來(lái)保持匹配的狀態(tài)被啼,但是\K左邊的字符不會(huì)被放在匹配中的部分幅狮。(其實(shí)相當(dāng)于一個(gè)后向斷言)
有些情況下非常有用:

#替換一部分
my $exclamation = 'This is a catastrophe!';
$exclamation =~ s/cat\K\w+!/./;
#只會(huì)替換\K后面的那部分潦嘶。

like( $exclamation, qr/\bcat\./, "That wasn't so bad!" );

正則修飾符

修飾符會(huì)改變正則表達(dá)式的行為漠趁,通常是在模式末尾使用椰于。
禁用大小寫(xiě)敏感:

my $pet = 'CaMeLiA';

like( $pet, qr/Camelia/, 'Nice butterfly!' );
like( $pet, qr/Camelia/i, 'shift key br0ken' );

也可以集成在模式中:

my $some='cat';
my $find_a_cat = qr/(?<feline>cat)/;

print $+{feline} if $some=~/$find_a_cat/;
#(?i)語(yǔ)法就表示忽略大小寫(xiě)
#禁用指定的修飾符可以在前面使用減號(hào)蚓耽,如(?-i)就表示必須大小寫(xiě)一致才會(huì)匹配歉闰。

其他的修飾符:

/m 啟用多行模式装悲,^和$匹配字符串內(nèi)的換行符椅您,而非字符串的開(kāi)頭和末尾
/s 點(diǎn)號(hào)可以匹配任何字符贱除,包括換行符
/r 非破壞方式替換字符

my $status = 'I am hungry for pie.';
my $newstatus = $status =~ s/pie/cake/r;
my $statuscopy = $status =~ s/liver and onions/bratwurst/r;

is( $status, 'I am hungry for pie.', 'original string should be unmodified' );
like( $newstatus, qr/cake/, 'cake wanted' );
unlike( $statuscopy, qr/bratwurst/, 'wurst not' );


/x 可以在模式中增加空白和注釋?zhuān)黾涌勺x性
my $attr_re = qr{
\A # start of line
(?:
[;\n\s]* # spaces and semicolons
(?:/\*.*?\*/)? # C comments
)*
ATTR
\s+
( U?INTVAL
| FLOATVAL
| STRING\s+\*
)
}x;


/g 全局替換
# appease the Mitchell estate
my $contents = slurp( $file );
$contents =~ s/Scarlett O'Hara/Mauve Midway/g;


\G 在上一個(gè)匹配處進(jìn)行匹配
while ($contents =~ /\G(\w{3})(\w{3})(\w{4})/g)
{
push @numbers, "($1) $2-$3";
}



/e 修飾符讓你具有更靈活的替換字符串能力生闲,由代碼運(yùn)行的返回值作為替代的字符串
$sequel =~ s{Scarlett( O'Hara)?}
{
'Mauve' . defined $1
? ' Midway'
: ''
}ge;
#匹配時(shí),執(zhí)行代碼并以返回值替換
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末月幌,一起剝皮案震驚了整個(gè)濱河市碍讯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扯躺,老刑警劉巖捉兴,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異录语,居然都是意外死亡倍啥,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)澎埠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)虽缕,“玉大人,你說(shuō)我怎么就攤上這事蒲稳〉鳎” “怎么了伍派?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)剩胁。 經(jīng)常有香客問(wèn)我拙已,道長(zhǎng),這世上最難降的妖魔是什么摧冀? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮系宫,結(jié)果婚禮上索昂,老公的妹妹穿的比我還像新娘。我一直安慰自己扩借,他們只是感情好椒惨,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著潮罪,像睡著了一般康谆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嫉到,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天沃暗,我揣著相機(jī)與錄音,去河邊找鬼何恶。 笑死孽锥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的细层。 我是一名探鬼主播惜辑,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼疫赎!你這毒婦竟也來(lái)了盛撑?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤捧搞,失蹤者是張志新(化名)和其女友劉穎抵卫,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體实牡,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陌僵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了创坞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碗短。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖题涨,靈堂內(nèi)的尸體忽然破棺而出偎谁,到底是詐尸還是另有隱情总滩,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布巡雨,位于F島的核電站闰渔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏铐望。R本人自食惡果不足惜冈涧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望正蛙。 院中可真熱鬧督弓,春花似錦、人聲如沸乒验。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锻全。三九已至狂塘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鳄厌,已是汗流浹背荞胡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留了嚎,地道東北人硝训。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像新思,于是被迫代替她去往敵國(guó)和親窖梁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容