Question Compter les groupes de capture dans une regex qr?


Je travaille sur un projet qui à un moment donné obtient une liste de fichiers depuis un serveur ftp. À ce stade, il retourne soit un tableau RAF de fichiers, soit une référence optionnelle regex (c'est-à-dire qr), est passé, il filtre la liste en utilisant grep. De plus si cela qr possède un groupe de capture, il traite la section capturée comme un numéro de version et renvoie à la place un hashref où les clés sont les versions et les valeurs sont les noms de fichiers (qui auraient été renvoyés comme tableau si aucun groupe de capture). Le code ressemble à (simplifié légèrement)

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

Cette implémentation tente de créer le hachage et le renvoie s'il réussit. Ma question, est-ce que je peux détecter que le qr a un groupe de capture et tente seulement de créer le hash si c'est le cas?


10
2017-12-28 15:57


origine


Réponses:


Vous pourriez utiliser quelque chose comme:

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

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

Sortie:

2

18
2017-12-28 16:30



Voir nparen dans 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;
}

Une autre possibilité pour éviter d’inspecter le modèle consiste à se fier simplement à la valeur de $capture. Ce sera 1 (Perl true value) en cas de correspondance réussie sans capture. Vous pouvez le distinguer de la capture occasionnelle de retour 1 parce que l'on manque le IV drapeau.


4
2017-12-28 17:03



Vous pourriez utiliser YAPE :: Regex analyser l'expression régulière pour voir s'il y a une capture présente:

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

3
2017-12-28 16:42