给出一个字符串,$contact_info
,包含有联系信息,你可以将$phone_number
正则表 达式应用其上并通过具名捕获来将任何匹配结果捕获并存入变量中:
if ($contact_info =~ /(?<phone>$phone_number)/)
{
say "Found a number $+{phone}";
}
捕捉结构可能看上去像是一大个摇晃的标点,当你可以将其作为整体认读时,它还是比较 简单的:
括号包围了整个捕获。?< name >
结构必须紧跟左括号。它为捕获缓冲区提供了名 称。此结构位于括号内的其余部分是一个正则表达式。如果当正则表达式匹配该片段,Perl 将字符串被捕获的部分存储在神奇变量%+
中:一个以捕获缓冲区名为键、匹配正则表 达式的字符串部分为值的哈希。
对于 Perl 5 正则表达式来说,括号是特殊的;默认和常规的 Perl 代码一样,它们的行为 就是进行分组。它们也将匹配部分组成的一个或多个原子包围在内。要在正则表达式内使用 字面括号,你必须添加反斜杠,就像$area_code
变量里那样。
编号捕获
具名捕获是 Perl 5.10 的新功能,但捕获早已在 Perl 中存在了许多年头。你也会碰到编号捕获:
if ($contact_info =~ /($phone_number)/)
{
say "Found a number $1";
}
括号把要捕获片段包围在内,但是没有正则表达式元字符给出捕获的名称。作为代替, Perl 将捕获的子字符串存放在一系列以$1
开头的神奇变量中,并延续至正则表达式 中提供所有捕获组。Perl 找到的第一个匹配捕获存放在$1
,第二个存放在$2
, 等等。捕获计数起始于捕获的开括号;因而第一个左括号将捕获存入$1
,第二个 存入$2
,等等。
虽然具名捕获的语法比编号捕获来得长一些,但它提供了额外的清晰度。你不需要统计开 括号的的个数来指出某捕获会被存入$4
还是$5
,并且基于较短的正则表达式编写 更长的正则表达式相对容易一些,因为它们通常对位置的变更或是否出现在单个原子中不 那么敏感。
当你将一处匹配在列表上下文中求值时,编号捕获相对不那么令人沮丧:
if (my ($number) = $contact_info =~ /($phone_number)/)
{
say "Found a number $number";
}
Perl 将按捕获顺序赋值给左值:
成组和选项
前面的例子将全部量词应用于简单原子上。它们也可以应用于一个更为复杂的子模式整体:
my $pork = qr/pork/;
my $beans = qr/beans/;
like( 'pork and beans',qr/\A$pork?.*?$beans/,'maybe pork,definitely beans' );
如果手动扩展该正则表达式,结果可能令你感到惊讶:
like( 'pork and beans',qr/\Apork?.*?beans/,definitely beans' );
这样仍然匹配,但考虑一个更为具体的模式:
my $pork = qr/pork/;
my $and = qr/and/;
my $beans = qr/beans/;
like( 'pork and beans',qr/\A$pork? $and? $beans/,maybe and,definitely beans' );
一些正则表达式不是匹配这项就是匹配另一项。使用选项元字符 (|
) 即可:
my $rice = qr/rice/;
my $beans = qr/beans/;
like( 'rice',qr/$rice|$beans/,'Found some rice' );
like( 'beans','Found some beans' );
选项元字符意味着匹配前述任一片段。但请注意解释为正则表达式片段的内容:
like( 'rice',qr/rice|beans/,'Found some rice' );
like( 'beans','Found some beans' );
unlike( 'ricb','Found some weird hybrid' );
模式rice|beans
可能会解释为ric
,后接e
或b
,再跟上eans
————但是,这是不正确的。选项总是将离正则表达式分隔符最近的算作整个片 段,无论该分隔符是模式开头和结尾,外围的括号,还是另一个选项字符或者中括号。
为了减少迷惑性,可以像变量 ($rice|$beans
) 这样使用具名片段,或者将候选项 包括在非捕获分组中:
my $starches = qr/(?:pasta|potatoes|rice)/;
(?:)
序列将一系列原子成组但跳过捕获行为。此例中,它包括了三个选项。
其他转义序列
Perl 将正则表达式内的若干字符解释为元字符,它们代表不同于他们字面形式的意义。 中括号总是标示一个字符类,括号则将片段成组且可选地进行捕获。
要匹配一个元字符的字面实例,可以用反斜杠(\
)对其进行转义。因此,\(
意指单个左括号而\]
意指单个右中括号。\.
指的是一个字面点号,而非“匹配除换行符 外所有字符”的原子。
其他通常需要转义的有用的元字符是管道符(|
)和美元符号($
)。同时不要忘记量词:*
、?
。
为避免处处转义(和担心忘记转义内插的值),可以使用元字符禁用字符。\Q
元字符 禁用对元字符的处理直到它碰到\E
序列。当取用你无法控制的正则表达式来匹配文本时, 这个个功能尤其有用:
my ($text,$literal_text) = @_;
return $text =~ /\Q$literal_text\E/;
$literal_text
参数可以包含任何内容————例如字符串** ALERT **
。使用\Q
和\E
,Perl 不会将“零或多个”量词解释为量词。相反,它会将此正则表达式解释为\*\* ALERT \*\*
并试图匹配字面星号。
@H_502_684@
在处理来自不可信任的用户输入时须特别小心。构造一个恶意正则表达式对你的程序进行 有效的拒绝服务攻击是完全可以办到的。
断言
正则表达式锚点(\A
和\Z
)是一种正则表达式断言的形式,字符串需要满足 此条件,但并不实际匹配字符串中的某个字符。就是说,正则表达式qr/\A/
将一直匹配,无论字符串内容为何。元字符\b
和\B
也是断言。
零宽度断言匹配一个模式,不仅仅是一个字符串中的条件。最重要的是,它们不消耗 它们匹配模式中的位置。例如,你只要找一只“cat(猫)”,你可以是用单词边界断言:
my $just_a_cat = qr/cat\b/;
……但如果想找一非灾难性的“cat”,你也许会用到零宽度否定前瞻断言:
my $safe_feline = qr/cat(?!astrophe)/;
(?!...)
结构仅在astrophe
不紧随其后时匹配短语cat
。
零宽度否定前瞻断言:
my $disastrous_feline = qr/cat(?=astrophe)/;
……仅在短语astrophe
紧随其后时匹配cat。这看上去不怎么有用,一个普通的正 则表达式就能完成同样的任务,但考虑下述情况,如果你想在字典中查找所有非“catastrophic” 但以cat
开头的单词。一种可能的情况是:
my $disastrous_feline = qr/cat(?!astrophe)/;
while (<$words>)
{
chomp;
next unless /\A(?<some_cat>$disastrous_feline.*)\Z/;
say "Found a non-catastrophe '$+{some_cat}'";
}
因为断言宽度为零,它不消耗源字符串。因此,带锚点的.*\Z
模式片段必须出现;否则 就只将捕获源字符串中的cat
部分。
零宽度后顾断言也是存在的。不像前瞻断言那样,这些断言的模式的长度必须固定;你不可 以在这些模式中使用量词。
要对你的猫绝不会出现在行首做出断言,你可以使用零宽度否定后顾断言:
my $middle_cat = qr/(?<!^>cat/;
……此处的(?结构包含定长模式。特别的,你可以用零宽度肯定后顾断言表达cat
必须总是立即出现在空格符之后:
my $space_cat = qr/(?<=\s)cat/;
……此处的(?<=...)
结构包含定长模式。这种方式在用\G
修饰符进行全局正则表 达式匹配时非常有用,但这是一个你不会经常用到的高级特性。
正则表达式修饰符
正则表达式操作符允许若干修饰符改变匹配的行为。这些修饰符出现在匹配、替换和qr//
操作符的结尾。例如,要启用大小写不敏感的匹配:
my $pet = 'CaMeLiA';
like( $pet,qr/Camelia/,'You have a nice butterfly there' );
like( $pet,qr/Camelia/i,'Your butterfly has a broken shift key' );
第一个like()
会失败,因为这些字符串包含不同的字母。第二个like()
将通过,因 为/i
修饰符使得正则表达式忽略大小写的区别。因修饰符的关系,M
和m
在第二 例中是等价的。
你也可以在模式中内嵌修饰符:
my $find_a_cat = qr/(?<feline>(?i)cat)/;
(?i)
语法仅为它所包围的组启用大小写不敏感匹配:此例中,即整个feline
捕获组。 你可以以此形式使用多个修饰符(在模式合适的部分)。你也可以通过前缀-
来禁用特定 的修饰符:
my $find_a_rational = qr/(?<number>(?-i)Rat)/;
多行操作符,/m
,允许^
和$
锚点匹配字符串中任意行开头和结尾。
/s
修饰符将源字符串作为一行对待,如此.
元字符便匹配换行符。Damian Conway 对助记符提出建议,/m
修改多个(multiple)正则表达式元字符的行为,而/s
修改单个(single)正则表达式元字符的行为。
/x
修饰符允许你在模式中内嵌额外的空白和注释而不会改变它们的原意。此修饰符生效时, 正则表达式引擎将空白和注释字符(#
)及其后的内容统统作为注释并忽略它们。这允许你编 写更可读的正则表达式:
my $attr_re = qr{
^ # 行首
# 杂项
(?:
[;\n\s]* # 空白和伪分号
(?:/\*.*?\*/)? # C 注释
)*
# 属性标记
ATTR
# 类型
\s+
( U?INTVAL
| FLOATVAL
| STRING\s+\*
| PMC\s+\*
| \w*
)
}x;
这个正则表达式不简单,但注释和空白提高了它的可读性。即便你利用已编译的片段一起 编写正则表达式,/x
修饰符还是能够帮助提高你的代码质量。
/g
修饰符对字符串从头到脚执行某正则表达式行为。它和替换一起使用时候比较合理:
# appease the Mitchell estate
my $contents = slurp( $file );
$contents =~ s/Scarlett O'Hara/Mauve Midway/g;
当和匹配一起使用时────并非替换────\G
元字符允许你在循环中按块处理字符串。\G
在最近一次匹配结束的位置进行匹配。为了按逻辑块处理一个全是美国电话号码的不正确编码 文件,你可以编写:
while ($contents =~ /\G(\w{3})(\w{3})(\w{4})/g)
{
push @numbers,"($1) $2-$3";
}
注意\G
锚点将从字符串中前一次迭代匹配的那一点开始着手。如果前一次匹配以诸如.*
之类的贪心匹配结束,则接下来的可以用于匹配的部分将减少。前瞻断言的使用在这里很重要, 因为它们不消耗欲匹配的字符串。
/e
修饰符允许你在替换操作右边写入任意 Perl 5 代码。如果成功匹配,正则表达式引擎将运 行这段代码,并用它的返回值作为替换的值。前面的全局替换例子中,替换不幸主角姓名的部分可 以变得更加健壮:
# appease the Mitchell estate
my $contents = slurp( $file );
$contents =~ s{Scarlett( O'Hara)?}
{ 'Mauve' . defined $1 ? ' Midway' : '' }ge;
你可以向一次替换操作添加任意多的/e
修饰符。每一处额外的修饰符将对表达式的结果进行又 一次的求值,通常只有 Perl 高尔夫手才会使用/ee
以及更加复杂的语句。
智能匹配
智能匹配操作符,~~
,对两个操作符进行比较并在互相匹配时返回真值。定义的模糊恰 好反映了此操作符的智能程度:比较操作由操作数两者共同决定。之前你已经见识了这种行 为────given
(Given/When)进行的就是隐式智能匹配。
智能匹配操作符是一个中缀操作符:
say 'They match (somehow)' if $loperand ~~ $roperand;
比较的类型大致先由右操作符的类型决定然后再是左操作符。例如,如果右操作符是一个 带数值成分的标量,则比较将使用数值等于。如果右操作符是一个正则表达式,则比较将 是一个 grep 操作或模式匹配。如果右操作符是一个数组,比较将是 grep 操作或递归的 智能匹配。如果右操作符是一个哈希,比较操作将检查一个或多个键是否存在。
例如:
# 标量数值比较
my $x = 10;
my $y = 20;
say 'Not equal numerically' unless $x ~~ $y;
# 标量类数值比较
my $x = 10;
my $y = '10 little endians';
say 'Equal numeric-ishally' if $x ~~ $y;
……以及:
my $needlepat = qr/needle/;
say 'Pattern match' if $needle ~~ $needlepat;
say 'Grep through array' if @haystack ~~ $needlepat;
say 'Grep through hash keys' if %hayhash ~~ $needlepat;
……再及:
say 'Grep through array' if $needlepat ~~ @haystack;
say 'Array elements exist as hash keys' if %hayhash ~~ @haystack;
say 'Array elements smart match' if @strawstack ~~ @haystack;
……又及:
say 'Grep through hash keys' if $needlepat ~~ %hayhash;
say 'Array elements exist as hash keys' if @haystack ~~ %hayhach;
say 'Hash keys identical' if %hayhash ~~ %haymap;
这些比较操作在某操作数是给出数据类型的引用时也能正常工作。举例来说:
say 'Hash keys identical' if %hayhash ~~ \%hayhash;
你可以在对象上重载(éè½½)智能匹配操作符。如果你不这样做,智能匹配 操作符在你尝试将某对象用作操作数时会抛出异常。
你也可以使用如undef
等其他数据类型,以及函数引用作为智能匹配操作数。请参 考perldoc perlsyn
中的表格来获取更多细节。