【问题标题】:Count the capture groups in a qr regex?计算 qr 正则表达式中的捕获组?
【发布时间】:2011-12-28 15:57:09
【问题描述】:

我正在从事一个项目,该项目有一次从 ftp 服务器获取文件列表。此时,它要么返回一个文件数组引用,要么如果传递了一个可选的正则表达式引用(即qr),它会使用 grep 过滤列表。此外,如果 qr 有一个捕获组,它将捕获的部分视为版本号并返回一个 hashref,其中键是版本,值是文件名(如果没有捕获,它将作为数组返回组)。代码看起来像(稍微简化)

sub filter_files {
  my ($files, $pattern) = @_;
  my @files = @$files;
  unless ($pattern) {
    return \@files;
  }

  @files = grep { $_ =~ $pattern } @files;
  carp "Could not find any matching files" unless @files;

  my %versions = 
    map { 
      if ($_ =~ $pattern and defined $1) { 
        ( $1 => $_ )
      } else {
        ()
      }
    } 
    @files;

  if (scalar keys %versions) {
    return \%versions;
  } else {
    return \@files;
  }
}

此实现尝试创建哈希并在成功时返回它。我的问题是,我是否可以检测到 qr 有一个捕获组,并且只有在有的情况下才尝试创建哈希?

【问题讨论】:

    标签: regex perl capture


    【解决方案1】:

    你可以使用类似的东西:

    sub capturing_groups{
        my $re = shift;
        "" =~ /|$re/;
        return $#+;
    }
    
    say capturing_groups qr/fo(.)b(..)/;
    

    输出:

    2
    

    【讨论】:

    • 让我看看我是否明白了:您将空字符串与空字符串或我的正则表达式相匹配。什么都没有让它匹配,但它仍然填充@+,然后将捕获组的数量作为元素的数量。我对吗?非常聪明!
    • (correction) ... 然后它比捕获的元素多一个(因为 $+[0] 是匹配项)但是由于 $#+ 比元素的数量少一,它返回正确的匹配数
    • 所以,只有perl 可以编译Perl,还不如将其扩展到只有perl 应该尝试解析Perl Regexps? :-) 喜欢它。
    • @BRPocock,我喜欢这种情绪,但实际上我认为其他人的功能可能也有效。可以通过这种方式完成,这很巧妙。对于更复杂的事情,perl 可能无法为我们返回足够的信息,因此了解解析器的存在和工作以及它们的局限性很重要。
    • 当然。不过,我确实更喜欢它的同音性,并且更希望有更多这样的自省(在任何/每种给定的语言中)。
    【解决方案2】:

    nparen in Regexp::Parser

    use strictures;
    use Carp qw(carp);
    use Regexp::Parser qw();
    my $parser = Regexp::Parser->new;
    
    sub filter_files {
        my ($files, $pattern) = @_;
        my @files = @$files;
        return \@files unless $pattern;
    
        carp sprintf('Could not inspect regex "%s": %s (%d)',
            $pattern, $parser->errmsg, $parser->errnum)
            unless $parser->regex($pattern);
    
        my %versions;
        @files = map {
            if (my ($capture) = $_ =~ $pattern) {
                $parser->nparen
                    ? push @{ $versions{$capture} }, $_
                    : $_
            } else {
                ()
            }
        } @files;
        carp 'Could not find any matching files' unless @files;
    
        return (scalar keys %versions)
            ? \%versions
            : \@files;
    }
    

    另一种避免检查模式的可能性是简单地依赖$capture 的值。在没有捕获的成功匹配的情况下,它将是1(Perl 真值)。您可以将其与偶尔返回 1 的捕获区分开来,因为该捕获缺少 IV 标志。

    【讨论】:

    • 与toolic相同,感谢您为此付出的辛勤工作,但我想我会接受Qtax的回答。在我看来,依赖 Perl 自己的正则表达式引擎实现似乎比解析更万无一失。不过谢谢!我真的希望答案最终会是这样的。
    【解决方案3】:

    您可以使用YAPE::Regex 解析正则表达式以查看是否存在捕获:

    use warnings;
    use strict;
    use YAPE::Regex;
    
    filter_files(qr/foo.*/);
    filter_files(qr/(foo).*/);
    
    sub filter_files {
        my ($pattern) = @_;
        print "$pattern ";
        if (has_capture($pattern)) {
            print "yes capture\n";
        }
        else {
            print "no capture\n";
        }
    }
    
    sub has_capture {
        my ($pattern) = @_;
        my $cap = 0;
        my $p = YAPE::Regex->new($pattern);
        while ($p->next()) {
            if (scalar @{ $p->{CAPTURE} }) {
                $cap = 1;
                last;
            }
        }
        return $cap;
    }
    
    __END__
    
    (?-xism:foo.*) no capture
    (?-xism:(foo).*) yes capture
    

    【讨论】:

    • 这有点符合我的预期,但 Qtax 似乎更简单,并且依赖更少。不过还是谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-24
    • 2018-03-11
    • 1970-01-01
    相关资源
    最近更新 更多