Perl正则表达式引用

qr//创建正则对象

因为可以在正则模式中使用变量替换,所以我们可以将正则中的一部分表达式事先保存在变量中。例如:
$str="hello worlds gaoxiaofang";
$pattern="w.*d";
$str =~ /$pattern/;
print "$&\n";

但是,这样缺陷很大,在保存正则表达式的变量中存放的特殊字符要防止有特殊意义。例如,当使用m//的方式做匹配分隔符时,不能在变量中保存/,除非转义。

perl提供了qr/pattern/的功能,它把pattern部分构建成一个正则表达式对象,然后就可以:

在正则表达式中直接引用这个对象

可以将这个对象保存到变量中,通过引用变量的方式来引用这个已保存好的正则对象

将引用变量插入到其它模式中构建更复杂的正则表达式

其中:

qr//的定界符斜线可以替换为其它符号,例如对称的括号类qr() qr{} qr<> qr[],一致的符号类qr%% qr## qr!! qr$$ qr"" qr''等。

但是使用单引号作为定界符时比较特殊(即qr'pattern'),它会将pattern部分使用单引号的方式去解析,例如变量$var无法替换,而是表示4个字符。但是正则表达式的元字符仍然起作用,例如$仍然表示行尾。

$str="hello worlds gaoxiaofang";

# 直接作为正则表达式
$str =~ qr/w.*d/;
print "$&\n";

# 保存为变量,再作为正则表达式
$pattern=qr/w.*d/;
$str =~ $pattern;    # (1)
$str =~ /$pattern/;  # (2)
print "$&\n";

# 保存为变量,作为正则表达式的一部分
$pattern=qr/w.*d/;
$str =~ /hel.* $pattern/;
print "$&\n";

还允许为这个正则对象设置修饰符,比如忽略大小写的匹配修饰符为i,这样在真正匹配的时候,就只有这一部分正则对象会忽略大小写,其余部分仍然区分大小写。
$str="HELLO wORLDs gaoxiaofang";

$pattern=qr/w.*d/i;        # 忽略大小写

$str =~ /HEL.* $pattern/;  # 匹配成功,$pattern部分忽略大小写
$str =~ /hel.* $pattern/;  # 匹配失败
$str =~ /hel.* $pattern/i;  # 匹配成功,所有都忽略大小写

qr如何构建正则对象

输出qr构建的正则引用,看看是怎样的结构:
$patt1=qr/w.*d/;
print "$patt1\n";

$patt2=qr/w.*d/i;    # 加上修饰符i
print "$patt2\n";

$patt3=qr/w.*d/img;  # 加上修饰符img
print "$patt3\n";

上面的print将输出如下结果:
(?^:w.*d)
(?^i:w.*d)
(?^mi:w.*d)

qr的作用实际上就是在我们给定的正则pattern基础上加上(?^:)并带上一些修饰符,得到的结果总是(?^FLAGS:pattern)。

但是上面patt3的修饰符g不见了。先可以看看(?^:)的作用:非捕获分组,并重置修饰符。重置为哪些修饰符?对于(?^FLAGS:)来说,只有这些修饰符"alupimsx"是可用的,即(?^alupimsx:):

如果给定的修饰符不在这些修饰符内,则不被识别,有时候会报错

如果给定的修饰符属于这几个修饰符,那么没有给定的修饰符部分将采用默认值(不同版本可能默认是否开启的值不同)

所以上面的g会被丢弃,甚至在进一步操作这个正则引用时,会报错。

既然qr给pattern部分加上了(?^:),那么当它们插入到其它正则中的时候,就能保证这一段是独立的,不受全局修饰符影响的模式。
$patt1=qr/w.*d/im;
$patt2=qr/hel.*d $patt1/i;
print "$patt2\n";    # 输出:(?^i:hel.*d (?^mi:w.*d))

正则引用作为标量的用法

既然qr//创建的正则对象引用是一个标量,那么标量可以出现的地方,正则引用就可以出现。例如,放进hash结构,数组结构。

例如,放进数组中形成一个正则表达式列表,然后给定一个待匹配目标,依次用列表中的这些模式去匹配。
use v5.10.1;
my @patterns = (
    qr/(?:Willie )?Gilligan/,
    qr/Mary Ann/,
    qr/Ginger/,
    qr/(?:The )?Professor/,
    qr/Skipper/,
    qr/Mrs?. Howell/,
);

my $name = 'Ginger';
foreach my $pattern ( @patterns ) {
    if( $name =~ /$pattern/ ) {
        say "Match!";
        print "$pattern";
        last;
    }
}

还可以将这些正则引用放进hash中,为每个pattern都使用key来标识一下,例如pattern1是用来匹配什么的:
use v5.10.1;
my %patterns = (
    Gilligan => qr/(?:Willie )?Gilligan/,
    'Mary Ann' => qr/Mary Ann/,
    Ginger => qr/Ginger/,
    Professor => qr/(?:The )?Professor/,
    Skipper => qr/Skipper/,
    'A Howell' => qr/Mrs?. Howell/,
);
my $name = 'Ginger';
my( $match ) = grep { $name =~ $patterns{$_} } keys %patterns;
say "Matched $match" if $match;

上面将grep语句的结果赋值给了一个标量,所以如果有多个Pattern能匹配$name,多次执行,$match的值将可能会不一样。

构建复杂的正则表达式

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/8e7761692b9b6655c4af91ee40e47475.html