如何使任意Perl正则表达式完全不捕获?(答案:你不能)

Jef*_*eff 8 regex perl

如何从Perl正则表达式字符串中的任意嵌套子组中删除捕获?我想将任何正则表达式嵌入到一个包络表达式中,该表达式将子正则表达式捕获为整个实体以及静态已知的后续组.我是否需要手动将正则表达式字符串转换为使用所有非捕获(?:)组(并希望我不会搞砸),或者是否有提供此功能的Perl正则表达式或库机制?

# How do I 'flatten' $regex to protect $2 and $3?
# Searching 'ABCfooDE' for 'foo' OK, but '((B|(C))fo(o)?(?:D|d)?)', etc., breaks.
# I.E., how would I turn it effectively into '(?:(?:B|(?:C))fo(?:o)?(?:D|d)?)'?
sub check {
  my($line, $regex) = @_;
  if ($line =~ /(^.*)($regex)(.*$)/) {
    print "<", $1, "><", $2, "><", $3, ">\n";
  }
}
Run Code Online (Sandbox Code Playgroud)

附录:我隐约知道的$&,$`以及$'与已被告知,如果可能避免他们,我没有访问${^PREMATCH},${^MATCH}${^POSTMATCH}在我的Perl 5.8环境.上面的例子可以使用这些方法划分为2/3块,更复杂的实例可以手动迭代这个,但我想如果可能的话我想要一个通用的解决方案.

接受的答案:我希望存在并且令人惊讶(至少对我来说)不是,是一个封装组,使其内容不透明,以便随后的位置反向引用将内容视为单个实体,并且名称引用被取消范围. 对于Perl 5.10+,gbacon有一个潜在有用的解决方法,FM显示了在特定情况下可以实现相同效果的任何版本的手动迭代机制,但j_random_hacker称它没有真正的语言机制来封装子表达式.

j_r*_*ker 8

一般来说,你不能.

即使您可以将所有(...)s转换为(?:...)s,这在一般情况下也不起作用,因为该模式可能需要反向引用:例如/(.)X\1/,匹配任何字符,后跟a X,后跟最初匹配的字符.

因此,如果没有Perl机制来"在事后"丢弃捕获的结果,则无法解决所有正则表达式的问题.你可以做的最好(或者如果你有Perl 5.10就可以做)是使用gbacon的建议,并希望为捕获缓冲区生成一个唯一的名称.


Gre*_*con 7

保护您关心的子模式的一种方法是使用命名捕获缓冲区:

此外,从Perl 5.10.0开始,您可以使用命名捕获缓冲区和命名反向引用.符号是(?<name>...)声明和\k<name>引用.您也可以使用撇号而不是尖括号来分隔名称; 并且您可以使用括号中的\g{name}反向引用语法.也可以通过绝对和相对数量来引用命名的捕获缓冲区.在模式之外,通过%+散列可以获得命名的捕获缓冲区.当相同的图案内的不同的缓冲液具有相同的名称,$+{name}并且\k<name>指的是最左边的定义的组.

在你的问题的背景下,check成为

sub check {
  use 5.10.0;  
  my($line, $regex) = @_;
  if ($line =~ /(^.*)($regex)(.*$)/) {
    print "<", $+{one}, "><", $+{two}, "><", $+{three}, ">\n";
  }
}
Run Code Online (Sandbox Code Playgroud)

然后用它来调用它

my $pat = qr/(?<one>(?<two>B|(?<three>C))fo(o)?(?:D|d)?)/;   
check "ABCfooDE", $pat;
Run Code Online (Sandbox Code Playgroud)

输出

<CfooD><C><C>


FMc*_*FMc 5

这不能解决一般情况,但您的具体示例可以使用/g标量上下文中的选项进行处理,这样您就可以将问题分成两个匹配,第二个匹配第一个左边的匹配:

sub check {
    my($line, $regex) = @_;
    my ($left_side, $regex_match) = ($1, $2) if $line =~ /(^.*)($regex)/g;
    my $right_side = $1 if $line =~ /(.*$)/g;
    print "<$left_side> <$regex_match> <$right_side>\n"; # <AB> <CfooD> <E123>
}

check( 'ABCfooDE123', qr/((B|(C))fo(o)?(?:D|d)?)/ );
Run Code Online (Sandbox Code Playgroud)