我有一个小问题。我有perl regexp个多个捕获组。其中一些人有量词(如' +
')。如果没有添加量词,那么@-
& @+
数组与捕获组的匹配位置很好地匹配,但如果添加量词,则仅检测到最后一个匹配。但我想拥有所有这些。
一个例子:
my $s = 'xx1a2b3cyy';
my $re = qr/^xx(\d\w)+/;
所以我想知道匹配是'1a', '2b', '3c'
在2,4,6。
简单匹配给出:
if ($s =~ $re) {
print "Match @-, @+\n";
for (my $i = 0; $i < @-; ++$i) {
print 'i: ', $i, " - '", substr($s, $-[$i], $+[$i] - $-[$i]), "\n";
}
}
给出:
Match 0 6, 8 8
i: 0 - 'xx1a2b3c
i: 1 - '3c
因此只记住最后一次捕获组匹配。
我的下一个简单的尝试是因为RE不同而不是我真正想要的:
$re = qr/(\d\w)/;
my @s = ($s =~ /$re/g);
print "RE: '@s'\n";
while ($s =~ /$re/g) {
print "Match @-, @+\n";
for (my $i = 0; $i < @-; ++$i) {
print 'i: ', $i, " - '", substr($s, $-[$i], $+[$i] - $-[$i]), "\n";
}
}
给出:
RE: '1a 2b 3c'
Match 2 2, 4 4
i: 0 - '1a
i: 1 - '1a
Match 4 4, 6 6
i: 0 - '2b
i: 1 - '2b
Match 6 6, 8 8
i: 0 - '3c
i: 1 - '3c
但这不是我想要的,因为它会匹配像'ZZ1aAA2bBB3cZZ'
这样的字符串。
所以我不得不将两者结合起来。我能得到的最好的东西:
$re = '^xx(?:\d\w)*?\G(\d\w)';
pos($s) = 2;
while ($s =~ m($re)g) {
print "Match pos: ", pos($s), ', G: ', $1, ", '@-', '@+'\n"
}
给出:
Match pos: 4, G: 1a, '0 2', '4 4'
Match pos: 6, G: 2b, '0 4', '6 6'
Match pos: 8, G: 3c, '0 6', '8 8'
这几乎很好,但为此我需要知道第一场可能比赛的位置。如果设置不正确,则无法匹配。如果我删除非贪婪的部分,我只能确定第一个位置:
$re = '^xx(\d\w)';
if ($s =~ m($re)) {
print "Match: '@-', '@+'\n";
}
给出:
Match: '0 2', '4 4'
所以$-[1]
给出了第一个位置,但为此我必须手动修改RE&#34;。
如果我在模式中添加代码执行,我几乎得到了我需要的东西:
use re 'eval';
$re = '^xx(\d\w)+(??{print "Code: <@-> <@+>\n"})';
$s =~ m($re) and print "Match\n";
给出:
Code: <0 6> <8 8>
Code: <0 4> <6 6>
Code: <0 2> <4 4>
为此,我需要添加(?{ code })
部分。
有人知道更简单的方法(我的意思是不需要修改原始RE)来获得具有量词的捕获组的所有可能匹配吗?
提前致谢!
答案 0 :(得分:1)
没有通用的解决方案;正则表达式引擎根本不存储必要的信息。你要求使用正则表达式作为解析器,这是不行的。
<svg width="200px" height="200px">
<g transform="translate(70,70)">
<path d="M -40,-40 l 80,0 l 0,80 l -80,0 l 0,-80 z" style="fill: gray"></path>
<g>
<text text-anchor="middle" dominant-baseline="middle" style="fill: white" transform="scale(2)">
<tspan>test</tspan>
</text>
</g>
</g>
</svg>
或
sub extract {
for ($_[0]) {
/^ xx /xg
or return ();
my @matches;
push @matches, $1 while /\G (\d\w) /xg;
return @matches;
}
}
如果您只想要这些职位,那就是一样。
sub extract {
my ($pairs) = $_[0] =~ /^xx((?:\d\w)+)/
or return ();
return unpack('(a2)*', $pairs);
}
或
sub extract {
for ($_[0]) {
/^ xx /xg
or return ();
my @matches;
push @matches, $-[1] while /\G (\d\w) /xg;
return @matches;
}
}
使用正则表达式即使非通用解决方案也非常困难。假设您有以下模式:
sub extract {
$_[0] =~ /^xx((?:\d\w)+)/
or return ();
return map { $-[1] + ( $_ - 1 )*2 } 1..length($1)/2;
}
正确的解决方案是:
xx(\d\w)+yy(\d\w)+zz
输出:
use Storable qw( dclone );
my $s = "xx1a2byy3c4dZZ...xx5a6byy7c8dzz";
local our $rv;
if (
$s =~ /
(?{ [] })
xx
(?: (\d\w) (?{ my $r = dclone($^R); push @{ $r->[0] }, $^N; $r }) )+
yy
(?: (\d\w) (?{ my $r = dclone($^R); push @{ $r->[1] }, $^N; $r }) )+
zz
(?{ $rv = $^R; })
/x
) {
say "\$1: @{ $rv->[0] }";
say "\$2: @{ $rv->[1] }";
}
等等
$1: 5a 6b
$2: 7c 8d
需要
(zz(\d\w)+)+
输出:
use Storable qw( dclone );
my $s = "zz1a2bzz3c4d";
local our $rv;
if (
$s =~ /
(?{ [] })
(?:
(?{ my $r = dclone($^R); push @$r, []; $r })
zz
(?: (\d\w) (?{ my $r = dclone($^R); push @{ $r->[-1] }, $^N; $r }) )+
)+
(?{ $rv = $^R; })
/x
) {
say "\$1: @$_" for @$rv;
}
答案 1 :(得分:0)
我想我可以对你看到的行为给出一些解释:
在第一个例子中,我只能看到一个捕获组。量词允许它多次使用,但它仍然是一个捕获组。因此匹配子模式的每个新出现都会覆盖先前在那里捕获的值。即使RE引擎已经在其后面推进,但是会发生回溯(例如,具有分支等的更高级模式),可能是现在再次访问的捕获组将改变。由于@-
和@+
将位置保存到捕获组(而不是发生子模式匹配),这可以解释为什么只包含子模式的最后一次出现。
您甚至可以使用已命名的子模式和%+
/ %-
来体验同样的事情。使用已经使用过的(?{ })
变得更加明显,至少在调试方面是这样。但use re 'debug'
适用于较短的正则表达式/字符串匹配。
因此,在匹配仍在进行中时,请注意回溯对捕获组的影响!
但如果你不必关心回溯,我可以想到用量词来处理捕获组的一种方法:
如果您的捕获组是(bla)
而量词{0,3}
,请将其转换为
(?:(bla)(?{ print $-[$#-],$+[$#-]."\n" })){0,3}
。
您实际上将子模式放入另一个(非捕获)组。如果RE引擎已完成,请执行与目前为止匹配的最后一个捕获组有关的代码。然后,周围组外的量词负责正确执行代码片段的次数。
所以你的例子变成了这个:
use Data::Dumper;
my $s = 'xx1a2b3cyy';
my @submatches;
sub getem { push @submatches, [$-[$#-],$+[$#-]]; }
$s =~ m/^xx(?:(\d\w)(?{ getem() }))+/;
print Dumper(\@submatches);
这也适用于以这种方式转换的多个捕获组:
my $s = 'xx1a2b3cyy4de5fg6hihhh2';
$s =~ m/^xx(?:(\d\w)(?{ getem() }))+yy(?:(\d\w{2})(?{ getem() }))+hh/;
如果捕获组包含更多捕获组,则必须调整使用的索引。这就是为什么我更喜欢名字捕获组。
希望这有帮助。