如何检测Perl Regexp中有多少个捕获组?

Tru*_*ueY 6 regex perl

我在脚本中有一堆 .我想知道它们中有多少个捕获组.更准确地说,我想知道在@ - 和@ +数组中添加了多少项,如果它们匹配之前实际在真正的匹配操作中使用它们.

一个例子:

'XXAB(CD)DE\FG\XX' =~ /(?i)x(ab)\(cd\)(?:de)\\(fg\\)x/
    and print "'@-', '@+'\n";
Run Code Online (Sandbox Code Playgroud)

在这种情况下,输出是:

'1 2 11', '15 4 14'
Run Code Online (Sandbox Code Playgroud)

所以匹配后我知道第0项是字符串的匹配部分,并且有两个捕获组表达式.在实际比赛之前是否可以知道?

我试着把注意力集中在开口支架上.所以我首先删除了'\\'模式,以便更容易检测到转义的括号.然后我删除了'\('字符串.然后来了'(?'.现在我可以计算剩余的开括号.

my $re = '(?i)x(ab)\(cd\)(?:de)\\\\(fg\\\\)x'; print "ORIG: '$re'\n";
'XXAB(CD)DE\FG\XX' =~ /$re/ and print "RE: '@-', '@+'\n";
$re =~ s/\\\\//g; print "\\\\: '$re'\n";
$re =~ s/\\\(//g; print "\\(: '$re'\n";
$re =~ s/\(\?//g; print "\\?: '$re'\n";
my $n = ($re =~ s/\(//g); print "n=$n\n";
Run Code Online (Sandbox Code Playgroud)

输出:

ORIG: '(?i)x(ab)\(cd\)(?:de)\\(fg\\)x'
RE: '1 2 11', '15 4 14'
\\: '(?i)x(ab)\(cd\)(?:de)(fg)x'
\(: '(?i)x(ab)cd\)(?:de)(fg)x'
\?: 'i)x(ab)cd\):de)(fg)x'
n=2
Run Code Online (Sandbox Code Playgroud)

所以我知道这个正则有2个捕获组.但也许有一种更简单的方法,这绝对不是完整的(例如,这种对待(?<foo>...)(?'foo'...)作为非caputre组).

另一种方法是转储regcomp函数的内部数据结构.也许包Regexp :: Debugger可以解决这个问题,但我无权在我的环境中安装软件包.

实际上正则是某些ARRAY引用的键,我想在实际应用正则之前检查引用的ARRAY是否包含适当数量的值.当然,这种检查可以在模式匹配之后立即完成,但如果我可以在脚本的加载阶段进行检查则更好.

感谢您的帮助和评论!

Tru*_*ueY 0

正如奥巴马先生所说:“是的,我们可以!”

我找到了一个不需要额外模块并处理所有可能的捕获组事件的解决方案(据我所知)。正如 ikegami 提到的,它需要重新解析正则表达式,但为我们做了这件事。

在 CPAN 上的 Perl 模块的大海捞针过程中,我发现了一个非常有趣的模块,称为warnings::regex::recompile。每次重新编译正则表达式时,它都会生成一条警告消息。分析来源我找到了问题的解决方案。

使用use re qw/Debug DUMP/;Perl 将解析后的正则表达式返回到STDERR. 在原始模块中,结果被转储到真实文件中,然后重新读取以进行处理。我修改了代码以使用内存中的文件。

我的解决方案是:

sub dumpre {
  use re qw(eval Debug DUMP);
  my $buf = ''; 

  open OLDERR, '>&', STDERR or die "$!";
  close STDERR or die "$!";
  open STDERR, '>', \$buf or die "$!";

  my $re = qr/$_[0]/;

  close STDERR or die "$!";
  open STDERR, '>&', OLDERR or die "$!";
  close OLDERR or die "$!";

  no re 'debug'; # Needed because of split

  return [ split '\n', $buf ];
}
Run Code Online (Sandbox Code Playgroud)

该函数在编译正则表达式时打开 DUMP。能够eval处理(?{...})(??{...})表达式。

my $re = 'aa(?:(a\d)+x)?((b\d)*d)*c*(d\d)?(e*)((f)+)(g)+';
my $r = dumpre $re;
print join "\n", @$r;
Run Code Online (Sandbox Code Playgroud)

结果是:

Compiling REx "aa(?:(a\d)+x)?((b\d)*d)*c*(d\d)?(e*)((f)+)(g)+"
Final program:
   1: EXACT <aa> (3)
   3: CURLYX[0] {0,1} (19)
   5:   CURLYM[1] {1,32767} (16)
   9:     EXACT <a> (11)
  11:     POSIXU[\d] (14)
  14:     SUCCEED (0)
  15:   NOTHING (16)
  16:   EXACT <x> (18)
  18: WHILEM (0)
  19: NOTHING (20)
  20: CURLYX[1] {0,32767} (40)
  22:   OPEN2 (24)
  24:     CURLYM[3] {0,32767} (35)
  28:       EXACT <b> (30)
  30:       POSIXU[\d] (33)
  33:       SUCCEED (0)
  34:     NOTHING (35)
  35:     EXACT <d> (37)
  37:   CLOSE2 (39)
  39: WHILEM[1/7] (0)
  40: NOTHING (41)
  41: STAR (44)
  42:   EXACT <c> (0)
  44: CURLYM[4] {0,1} (55)
  48:   EXACT <d> (50)
  50:   POSIXU[\d] (53)
  53:   SUCCEED (0)
  54: NOTHING (55)
  55: OPEN5 (57)
  57:   STAR (60)
  58:     EXACT <e> (0)
  60: CLOSE5 (62)
  62: OPEN6 (64)
  64:   CURLYN[7] {1,32767} (74)
  66:     NOTHING (68)
  68:     EXACT <f> (0)
  72:   WHILEM (0)
  73:   NOTHING (74)
  74: CLOSE6 (76)
  76: CURLYN[8] {1,32767} (86)
  78:   NOTHING (80)
  80:   EXACT <g> (0)
  84: WHILEM (0)
  85: NOTHING (86)
  86: END (0)
anchored "aa" at 0 floating "fg" at 2..9223372036854775807 (checking floating) minlen 4 
Run Code Online (Sandbox Code Playgroud)

OPEN\d+因此,带有, CURLYM[\d+],的行CURLYN[\d+]显示捕获括号表达式(行语法:segment_no:正则表达式命令(下一段))。(注意:CURLYX 是一个非捕获括号表达式,如 (?:...)+)。OPEN/CURLY[MN}后面的数字表示捕获组的序号。必须找到最后一个。在本例中为 8。

不幸的是,它不处理是否(??{...})返回括号表达式,但现在我并不需要这样做。我认为格式不固定,因此不同版本可能有所不同。但对我来说没关系。