代码之家  ›  专栏  ›  技术社区  ›  Jeff

如何使任意Perl regex完全不捕获?(回答:你不能)

  •  8
  • Jeff  · 技术社区  · 14 年前

    (?:) 组(希望我不要搞砸了),或者是否有一个Perl regex或库机制提供了这一点?

    # 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";
      }
    }
    

    我隐约知道 $& , $` $' 有人建议我尽量避开他们,但我没法接近他们 ${^PREMATCH} , ${^MATCH} ${^POSTMATCH}

    我希望存在但令人惊讶的是(至少对我来说)不存在的是一个封装组,它使其内容不透明,这样后续的位置反向引用将内容视为单个实体,而名称引用将被取消作用域。 gbacon 为Perl5.10+提供了一个可能有用的解决方案,以及 FM 显示了任何版本的手动迭代机制,可以在特定情况下实现相同的效果,但是 j_random_hacker 称之为没有真正的语言机制来封装子表达式。

    6 回复  |  直到 4 年前
        1
  •  8
  •   Community Paul Sweatte    7 年前

    即使你能改变一切 (...) 进入 (?:...) s、 这在一般情况下不起作用,因为 该模式可能需要反向引用 :例如。 /(.)X\1/ ,匹配任何字符,后跟 X

    因此,如果没有“事后”丢弃捕获结果的Perl机制,就无法解决所有regex的问题。最好的方法是使用 gbacon's suggestion 并希望为捕获缓冲区生成一个唯一的名称。

        2
  •  8
  •   Greg Bacon    14 年前

    保护您关心的子模式的一种方法是使用 named capture buffers :

    此外,从Perl5.10.0开始,您可以使用命名捕获缓冲区和命名反向引用。符号是 (?<name>...) 宣布并 \k<name> \g{name} 反向引用语法。也可以通过绝对数和相对数引用命名的捕获缓冲区。在模式之外,通过 %+ $+{name} 请参阅最左侧定义的组。

    就你的问题而言, check

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

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

    输出

    <CfooD><C><C>
        3
  •  5
  •   FMc TLP    14 年前

    这并不涉及一般情况,但您的特定示例可以使用 /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)?)/ );
    
        4
  •  2
  •   Sean    14 年前

    @- @+ 将偏移量放入匹配字符串的数组:

    sub check {
        my ($line, $regex) = @_;
        if ($line =~ /$regex/) {
            my $pre   = substr $line, 0, $-[0];
            my $match = substr $line, $-[0], $+[0] - $-[0];
            my $post  = substr $line, $+[0];
            print "<$pre><$match><$post>\n";
        }
    }
    
        5
  •  2
  •   brian d foy JRFerguson    4 年前

    PerlV5.22及更高版本具有 /n

        6
  •  0
  •   nicomen    14 年前

    这不会禁用捕获,但可能会实现您想要的:

    $ perl -wle 'my $_ = "123abc"; /(\d+)/ && print "num: $1"; { /([a-z]+)/ && print "letter: $1"; } print "num: $1";'
    num: 123
    letter: abc
    num: 123