Считать группы захвата в регулярном выражении qr? - PullRequest
10 голосов
/ 28 декабря 2011

Я работаю над проектом, который в какой-то момент получает список файлов с 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 ]

18 голосов
/ 28 декабря 2011

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

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

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

Выход:

2
4 голосов
/ 28 декабря 2011

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

3 голосов
/ 28 декабря 2011

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