Считать группы захвата в регулярном выражении qr?

Я работаю над проектом, который в какой-то момент получает список файлов с FTP-сервера. В этот момент он либо возвращает массив ссылок на файлы ИЛИ, если необязательная ссылка на регулярное выражение (т.е. qr), пропускает, фильтрует список, используя grep. Дальше если что qr имеет группу захвата, он обрабатывает захваченный раздел как номер версии и возвращает вместо него хэш-ссылку, где ключи - это версии, а значения - имена файлов (которые были бы возвращены как массив, если бы не было групп захвата). Код выглядит так (немного упрощен)

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 имеет группу захвата и пытается создать хэш, только если это так?

3 ответа

Решение

Вы можете использовать что-то вроде:

sub capturing_groups{
    my $re = shift;
    "" =~ /|$re/;
    return $#+;
}

say capturing_groups qr/fo(.)b(..)/;

Выход:

2

Увидеть nparen в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 true value) в случае успешного совпадения без захвата. Вы можете отличить его от случайного возвращения 1 потому что этого не хватает IV флаг.

Вы можете использовать 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
Другие вопросы по тегам