Perl 如何使用量词获取所有捕获组的位置?

Perl 如何使用量词获取所有捕获组的位置?,perl,regexp,regex,Perl,Regexp,Regex,我有个小问题。我有一个有多个捕获组的组。其中一些有量词(如“+”)。如果未添加量词,则@-&@+数组将很好地填充捕获组的匹配位置,但如果添加了量词,则仅检测到最后一个匹配。但是我想要所有的 例如: my $s = 'xx1a2b3cyy'; my $re = qr/^xx(\d\w)+/; 所以我想知道的是,在2,4,6处的匹配是'1a',2b',3c' 简单匹配可提供: if ($s =~ $re) { print "Match @-, @+\n"; for (my $i = 0;

我有个小问题。我有一个有多个捕获组的组。其中一些有量词(如“
+
”)。如果未添加量词,则
@-
&
@+
数组将很好地填充捕获组的匹配位置,但如果添加了量词,则仅检测到最后一个匹配。但是我想要所有的

例如:

my $s = 'xx1a2b3cyy';
my $re = qr/^xx(\d\w)+/;
所以我想知道的是,在2,4,6处的匹配是
'1a',2b',3c'

简单匹配可提供:

if ($s =~ $re) {
  print "Match @-, @+\n";
  for (my $i = 0; $i < @-; ++$i) {
    print 'i: ', $i, " - '", substr($s, $-[$i], $+[$i] - $-[$i]), "\n";
  }
}
因此,只会记住最后一次捕获组匹配

我的下一个简单尝试是,这不是我真正想要的,因为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";
  }
}
但这不是我想要的,因为它将匹配像
'zz1aa2bb3czz'
这样的字符串

所以我必须把两者结合起来。我能得到的最好的:

$re = '^xx(?:\d\w)*?\G(\d\w)';
pos($s) = 2;
while ($s =~ m($re)g) {
  print "Match pos: ", pos($s), ', G: ', $1, ", '@-', '@+'\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
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'
Code: <0 6> <8 8>
Code: <0 4> <6 6>
Code: <0 2> <4 4>
这几乎很好,但为此我需要知道第一场可能的比赛的位置。如果设置不正确,它将不匹配任何内容。如果我移除非贪婪部分,我只能确定第一个位置:

$re = '^xx(\d\w)';
if ($s =~ m($re)) {
  print "Match: '@-', '@+'\n";
}
其中:

Match: '0 2', '4 4'
因此,
$-[1]
给出了第一个位置,但为此,我必须“手动”修改RE

如果我将代码执行添加到模式中,我几乎可以得到我需要的:

use re 'eval';
$re = '^xx(\d\w)+(??{print "Code: <@-> <@+>\n"})';
$s =~ m($re) and print "Match\n";
使用“评估”;
$re='^xx(\d\w)+(?{print“Code:\n”})';
$s=~m($re)并打印“匹配\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
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'
Code: <0 6> <8 8>
Code: <0 4> <6 6>
Code: <0 2> <4 4>
代码:
代码:
代码:
为此,我需要添加
(?{code})
部分

有人知道一种更简单的方法(我的意思是不需要修改原始RE)来获得具有量词的捕获组的所有可能匹配项吗


提前谢谢

没有通用的解决方案;regex引擎根本不存储必要的信息。您要求使用正则表达式作为解析器,这是不可能的



如果你只是想要这些位置,它是一样的

sub extract {
   for ($_[0]) {
      /^ xx /xg
         or return ();

      my @matches;
      push @matches, $-[1] while /\G (\d\w) /xg;
      return @matches;
   }
}


即使是非通用的解决方案,也很难使用正则表达式。假设您有以下模式:

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
$1: 1a 2b
$1: 3c 4d
什么的

(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: 5a 6b
$2: 7c 8d
$1: 1a 2b
$1: 3c 4d

我想我可以对你看到的行为做出一些解释:

在第一个示例中,我只能看到一个捕获组。量词允许它被多次使用,但它仍然是一个捕获组。因此,匹配子模式的每一次新出现都会覆盖先前捕获的值。即使重新引擎已经在其后面进行了升级,但会发生回溯(例如,具有分支等的更高级模式),现在再次访问的捕获组可能会发生更改。由于
@-
@+
占据了捕获组的位置(与发生子模式匹配相反),这就解释了为什么只发生最后一次包含的子模式

您甚至可以使用命名的子模式和
%+
/
%-
,体验同样的事情。对于已经使用的
(?{})
,这一点变得更加明显,至少出于调试目的。但是
使用re'debug'
可以匹配较短的正则表达式/字符串

因此,在匹配仍在进行时,请注意回溯对捕获组的影响

但如果你不必关心回溯,我可以想出一种方法,用量词来处理捕获组:

如果您的捕获组是
(bla)
,并且您的量词
{0,3}
,请将其转换为

(?:(bla)(?{print$-[$#-],$+[$#-]。“\n”})){0,3}

实际上,您将子模式放入另一个(非捕获)组中。如果重新引擎已经完成,则执行有关到目前为止匹配的最后一个捕获组的代码。然后,周围组之外的量词负责代码片段的正确执行次数

所以你的例子变成了这样:

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/;
如果捕获组包含更多捕获组,则必须调整使用的索引。这就是为什么我更喜欢名称捕获组


希望这有帮助。

my@matches=('xx1a2b3cyy'=~m/^xx(\d\w)+/)仅包含
3c
。在上使用
use re'debug'
告诉我们,它将
1a
2b
3c
作为子模式的匹配项,但我不知道它们最终会出现在哪里。看起来第一个子模式的最后一个匹配项仅保存为
$1
,这解释了为什么
@+
@-
只指向它。@simbabque是的,这与“简单匹配给予”相同:codelet给予。无论如何,如果没有“g”修饰符,它不应该返回更多的值。但是如果你在我的示例中添加一个
/g
,你也只能得到最后一个。@simbabque:是的,你可以得到最后一个匹配项,而不是全部。这对我没有帮助,因为我想拥有一切。似乎只有
(?{code})
可以帮助。。。我必须用quantifier检查它是如何与多个捕获组一起工作的。您仍然必须检查整体匹配情况-如果RE引擎达到,嵌入的代码会立即执行。在
+
之后放置
z
会导致失败,但是
@子匹配
仍然包含所有触发的子匹配,而不是
z
(*fail)
,因此回溯引擎将尝试所有匹配。我的观点似乎没有被理解:如果整个匹配失败,捕获的位置可能不是你要找的。或者我遗漏了什么?仍然使用
(?{})
,问题特别要求删除它。如果您要使用
(?{})
,那么您完全错了——回溯不会删除存储的匹配项。你必须使用
$^R
(你应该使用
$^N
,而不是那种复杂的快车