使用Perl正则进行匹配
在Perl中使用正则表达式进行匹配是非常简单的一件事。例如:
"abc123def" =~ /\d+/;
上面示例会使用正则表达式\d+
去匹配字符串abc123def
。其中=~
是正则匹配操作符,左边是待匹配的字符串数据,右边是双斜线包围的正则表达式。
Perl中,正则匹配之后,默认不会返回所匹配成功的内容,而是返回一个代表着本次正则匹配是否成功的数据,因此正则匹配可直接用来做条件判断。实际上,Perl正则匹配的返回值问题比较复杂,稍后会详细解释。
my @names = qw(junma jinlong tuner fairy wugui);
my @names1;
for(@names){
push $_,@names1 if($_ =~ /[u-z]/);
}
say "@names1";
当正则表达式要匹配的是变量$_
,则可以直接简写为正则表达式。也就是说,$_ =~ /reg/
和/reg/
是等价的。
因此,上面筛选出包含uvwxyz字符的名字的示例,等价于如下简短代码:
my @names = qw(junma jinlong tuner fairy wugui);
my @names1 = grep {/[u-z]/} @names;
say "@names1";
严格来说,Perl中正则表达式的书写方式为m//
,其中斜线可以替换为其它符号,规则如下:
- 双斜线可以替换为任意其它成对符号,例如可以是对称的各种括号
m() m{}
,也可以是相同的字符m!! m%%
- 当采用双斜线时,可省略前缀m字母,即
//
等价于m//
- 如果正则表达式中出现了和分隔符相同的字符,需转义表达式中的符号,但建议换分隔符,例如
/http:\/\//
转换成m%http://%
使用qr创建正则表达式
Perl中除了可以将正则写为m/reg/
或省略m的/reg/
,还可以通过qr
来构建正则表达式。
qr
和q、qq、qw类似,只不过它构建的是正则表达式的字面量。例如:
qr/abc.*def/
qr(abc.*def)
qr{ab.*def}
此外,可以在正则模式中使用变量替换,所以可以将正则的一部分表达式事先保存在变量中。例如:
$str="hello worlds junmajinlong";
$pattern="w.*d";
$str =~ /$pattern/;
但这样做缺陷很大,必须要确保插入在正则表达式中的变量pattern中没有存放具有特殊意义的字符。例如,当使用m//
格式的正则进行匹配时,不能在变量中保存/
,除非转义。
Perl提供了qr/pattern/
的功能,它把pattern部分构建成一个正则表达式对象,然后就可以在正则表达式中直接引用这个对象,更方便的是可以将这个对象保存到变量中,通过引用变量的方式来引用这个已保存好的正则对象。
$str="hello worlds junmajinlong";
# 直接作为正则表达式
$str =~ qr/w.*d/;
# 保存为变量,再作为正则表达式
$pattern=qr/w.*d/;
$str =~ $pattern; # (1)
$str =~ /$pattern/; # (2)
# 保存为变量,作为正则表达式的一部分
$pattern=qr/w.*d/;
$str =~ /hel.* $pattern/;
还允许为这个正则对象设置修饰符,比如忽略大小写的匹配修饰符为i,这样在真正匹配的时候,就只有这一部分正则对象会忽略大小写,其余部分仍然区分大小写。
$str="HELLO wORLDs gaoxiaofang";
$pattern=qr/w.*d/i; # 忽略大小写
$str =~ /HEL.* $pattern/; # 匹配成功,$pattern部分忽略大小写
$str =~ /hel.* $pattern/; # 匹配失败
$str =~ /hel.* $pattern/i; # 匹配成功,所有都忽略大小写
小心正则表达式中的特殊符号
正则表达式中使用了很多元字符,这些元字符在正则表达式中有特殊含义。另外,Perl自身也使用了很多特殊符号。如果在正则表达式中使用了两者冲突的特殊符号,就需要特殊处理。
例如,可以在正则表达式中内插变量:
$str =~ /hel.*$pattern/;
但如果是下面这样的正则表达式呢?
$str =~ /abc$\ndef/m;
这里有两种解析方式:
- 将
$\
看作变量,等价于/abc${\}ndef/m
- 在多行模式下,匹配abc以及下一行def
Perl会默认解析为前者,如果确实想要的是前者,在正则表达式中内插变量,都建议使用${var}
的变量引用方式。
如果想要的结果是后者,则可改写为:
$str =~ /abc$(?:)\ndef/m;
即,使用一个空的非捕获分组括号将$
符号和\
隔开。
更好的方式是将正则表达式部分以单引号的方式定义为变量,然后在正则表达式中内插该变量:
my $re_str = q%abc$\ndef%;
$str =~ /${re_str}/m;
有时候并不是想要将Perl中的特殊字符转义,而是想要将正则表达式中的特殊字符转义。此时可使用反斜线转义元字符,使其被当作普通字面符号。如果要转义的元字符较多,可使用Perl提供的\Q...\E
进行强转。但注意,\Q
仍然无法转义变量内插。
$sub="world";
$str="hello worlds junmajinlong";
$str =~ /\Q$sub\E/; # $sub会替换,所以匹配成功world
$str =~ /\Q$sub.\E/; # 元字符"."被当做普通的点符号,所以无法匹配