- 正则表达式匹配中的双斜线定界符
//
其实是m //
的简写,就像给列表赋值的qw //
一样,m //
也可以写成m {}
,m ()
,m <>
,m % %
,但只有m //
可以简写成//
。
如果使用了双斜线//
或者尖括号< >
做定界符,在模式中如果要匹配它们自身时,即使它们并不是正则表达式的元字符,也需要反斜线转义以避免Perl混淆,提早结束模式。
demo8-1
#!/usr/bin/perl
$_ = "http://www.baidu.com";
if(m {http://}){
print "matched m {http://}\n";
}
if(m /http:\/\//){
print "matched m /http:\\/\\//\n";
}
用花括号{}
做定界符时模式内的斜线不需要转义,但是用斜线/
做定界符时,模式内的斜线就要转义
./demo8-1
matched m {http://}
matched m /http:\/\//
- 在正则表达式的模式后用修饰符可以改变模式匹配的默认行为,有以下几种:
/i
:不区分大小写
/s
:让.
匹配任意字符(默认.
不匹配换行符)
/x
:让模式里的空格和制表符不参与匹配(但是\s依然能匹配空格和制表符),目的在于允许模式中随意添加空白,以更利于阅读
修饰符
demo8-2
#!/usr/bin/perl
$_ = "ABC DEF GHI";
print $_."\n";
if(/abc def ghi/){
print "matched:/abc def ghi/\n";
}
if(/abc def ghi/i){
print "matched:/abc def ghi/i\n";
}
if(/abc def ghi/ix){
print "matched:/abc def ghi/ix\n";
}
if(/abc\sdef\sghi/ix){
print "matched:/abc\\sdef\\sghi/ix\n";
}
使用/x
修饰符后,模式里的空格失效,但是\s
依然可以匹配
./demo8-2
ABC DEF GHI
matched:/abc def ghi/i
matched:/abc\sdef\sghi/ix
- 锚位用来限定模式匹配的位置
-
^
限定匹配位置在字符串的开头(^
在字符集[]
里也有使用,表示字符集的补集) -
$
限定匹配位置在字符串的结尾
demo8-3
#!/usr/bin/perl
$_ = "abc\n";
if(/^abc$/){
print "matched\n";
}
$
同时匹配字符串结尾和字符串结尾的换行符\n
./demo8-3
matched
锚位^
和$
默认是匹配字符串的开头和结尾,字符串可能还有多个换行符,修饰符/m
可以让^
和$
只匹配到每行的开头的结尾
\b
和\B
匹配单词和非单词首尾(单词在Perl中指连续的字母[a-zA-Z]
、数字[0-9]
和下划线_
),也就是说,\b
会匹配\w\W
和 \W\w
,而\B
会匹配\W\W
和\w\w
demo8-4
#!/usr/bin/perl
$_ = "zhangsan1999_ zhang_san_1999";
print $_."\n";
if(/\bzhangsan1999_\b/){
print "matched \\bzhangsan1999_\\b\n";
}
if(/\bzhang\B_\Bsan_\B1999\b/){
print "matched \\bzhang\\B_\\Bsan_\\B1999\\b\n";
}
./demo8-4
zhangsan1999_ zhang_san_1999
matched \bzhangsan1999_\b
matched \bzhang\B_\Bsan_\B1999\b
- 正则表达式的默认行为是用模式匹配
$_
中的字符串,但可以用绑定符号=~
改变这一行为,用=~
右边的模式,匹配=~
左边的字符串
demo8-5
#!/usr/bin/perl
$new_string = "Today is wonderful!";
if($new_string =~ m{Today is wonderful!}){
print "matched!\n";
}
./demo8-5
matched!
- 正则表达式的模式里可以内插标量变量,但需要小心标量里可能存在的元字符
demo8-6
#!/usr/bin/perl
$_ = "I'm fred";
print $_."\n";
$bettyorfred = "betty|fred";
if(/^$bettyorfred/){
print "matched ^betty|fred\n";
}
if(/^($bettyorfred)/){
print "matched ^(betty|fred)\n";
}
^betty|fred
在字符串开头位置匹配betty
或在字符串任意位置匹配fred
^(betty|fred)
在字符串开头位置匹配betty
或fred
./demo8-6
I'm fred
matched ^betty|fred
- 正则表达式模式中的圆括号
()
不仅可以用来实现分组,也可以实现捕获功能,捕获是指把当前圆括号内模式匹配到的内容暂时存储起来的功能,捕获记录的是模式匹配到的字符串,而不是模式本身,如果没有匹配的字符串,则对应的记录就为空
demo8-7
#!/usr/bin/perl
$_ = "Fred fred";
if(/([a-zA-Z])(red) f\2/){
print "$1red matched\n";
}
捕获用$1 $2 ...
来记录多个分组的捕获结果,和反向引用\1 \2 ...
不同的是,捕获功能是在正则匹配结束之后把捕获的结果存储在预定义的变量里,而反向引用是在正则表达式的模式内引用已经匹配的结果,超出正则表达式模式匹配的范围,反向引用就失去了它的意义,因为反向引用的语法只存在于正则表达式内,在Perl语言中,是没有\1 \2 ...
这样的语法的。
./demo8-7
Fred matched
捕获变量$1 $2 ...
的生命周期从本次匹配成功开始到下次匹配成功为止,也就是说,失败的匹配不会改变上次成功匹配捕获的值,而成功的匹配会将它们重置为新的捕获值
demo8-8
#!/usr/bin/perl
$_ = "abc def";
if(/(c d)/){
print "match:$1\n";
}
$_ =~ /(a_b)/;
print "match:$1\n";
$_ =~ /(bc de)/;
print "match:$1\n";
第一次匹配成功后$1
里是c d
,第二次匹配失败,$1
里的内容没有改变,第三次匹配成功,$1
里的内容变成了bc de
./demo8-8
match:c d
match:c d
match:bc de
所以,如果想要在匹配之后继续使用匹配的内容,最好的方式就是及时把捕获的内容保存在自己定义的变量里
捕捉功能也可以手动关闭,只需要在圆括号开始的位置键入?:
这样该圆括号只用于分组,而不会把捕获的内容存入对应的捕获变量中去
demo8-9
#!/usr/bin/perl
$_ = "This is a test string";
if(/^(That|This) .* (str)\w*/){
print "I only focused $2\n";
}
if(/^(?:That|This) .* (str)\w*/){
print "I only focused $1\n";
}
我只需要打印匹配到的str
字段,而不需要把$1
预留给我不关注的第一个分组匹配到的是That还是This
./demo8-9
I only focused str
I only focused str
更进一步,我们还可以脱离$1, $2 ...
这些编号的束缚,Perl 5.10引入了命名捕捉可以给要捕获的内容,分配一个名字。Perl将采用哈希的方式存储捕获的结果,哈希表%+
默认用来保存正则捕获的结果,其中键是命名,值是捕获的字符串
使用命名捕捉的方式是在圆括号内的开头写上 g<NAME>
,NAME就是自定义的名字,由于采用了哈希,所以名称不能重复。
demo8-10
#!/usr/bin/perl
$_ = "This is a test";
if(/(This|That) (?<be>is|was) (?<one>a|an) (test)/){
print "This $+{be} $+{one} test\n";
}
./demo8-10
This is a test
Perl有趣的特性在于,使用了命名捕捉之后,不仅在捕获的功能里可以用,在反向引用的功能中也可以引用,用\g{NAME}
或\k{NAME}
(二者等价)取代\g{1}
来表示对应的分组
demo8-11
#!/usr/bin/perl
$_ = "gred's name is gred";
if(/^(?<gname>gred)'s name is \g{gname}$/){
print "matched!\n";
}
./demo8-11
matched!
Perl还提供了自动捕捉变量功能
demo8-12
#!/usr/bin/perl
if("1Monday 2Tuesday 3Wednesday 2TuesDay 4Thursday" =~ /Tuesday/){
print "1Monday 2Tuesday 3Wednesday 2TuesDay 4Thursday\n";
print "\$`=$`\n";
print "\$&=$&\n";
print "\$'=$'\n";
}
$&
保存第一次匹配到的字符串内容
$`
保存$&
之前的字符串内容
$'
保存$&
之后的字符串内容
./demo8-12
1Monday 2Tuesday 3Wednesday 2TuesDay 4Thursday
$`=1Monday 2
$&=Tuesday
$'= 3Wednesday 2TuesDay 4Thursday
- 模式中的量词代表前置条目的重复次数
{X}
:匹配X次(超过X个会只匹配前X个)
{M,N}
:至少匹配M次,至多匹配N次
{M,}
:至少匹配M次
(,N)
:至多匹配N次
{0,}
或*
:匹配任意次
{1,}
或+
:至少匹配1次
{0,1}
或?
:不匹配或至多匹配一次 - 模式测试程序
demo8-13
#!/usr/bin/perl
while(<>){
chomp;
if(/PATTERN/){
print "Matched:|$`<$&>$'|\n";
} else{
print "No Match:|$_|\n";
}
}