0
votes

sub qui renvoie un groupe regex correspondant

J'analyse les lignes du formulaire

sub _getid ($) {
    $_[0] =~ /\d+:\d+ \w+: (\d+)/;
    $1; # or return $1;
}

J'ai un sous-gestionnaire qui obtient juste une ligne et utilise given/when pour le passer à un sous-gestionnaire plus spécifique basé sur des correspondances regex - par exemple, la ligne ci-dessus serait passée au sous _someevent .

Dans ces sous-gestionnaires spécifiques, je voudrais extraire la partie 0 de la ligne, qui est comme un ID.

J'ai écrit le sous-marin suivant à cet effet:

my $id = _getid("12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n");
say "ID = $id";

Ce sous semble fonctionner lorsqu'il est utilisé comme ceci:

say _getid("12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n");

Mais quand j'attribue le résultat à une variable:

sub _getid ($) { $_[0] =~ /\d+:\d+ \w+: (\d+)/ }

il se transforme toujours en 1 . Je crois que cela a à voir avec le fait que la correspondance =~ regex renvoie en fait une liste ou quelque chose et je l'assigne à un scalaire ...?

J'ai plutôt proposé ce qui suit:

12:34 SomeEvent: 0 Lorem ipsum dolor sit amet

Mais il doit y avoir une manière meilleure et élégante de s'attaquer au problème.


3 commentaires

Les prototypes Perl ne font probablement pas ce que vous pensez faire, et vous ne devriez probablement pas les utiliser.


Le regex que vous dites que vous utilisez ne correspond pas à l'exemple de chaîne que vous fournissez. Il manque un deux-points dans l'expression régulière.


@TLP en effet, merci pour le heads-up


4 Réponses :


3
votes

Vous êtes brûlé par le contexte. Depuis perlop (en particulier, la section sur les opérateurs de type devis Regexp ):

/ MOTIF / msixpodualngc

Recherche une chaîne pour une correspondance de modèle et, dans un contexte scalaire, renvoie true si elle réussit, false si elle échoue.

Et ensuite:

Correspondance dans le contexte de la liste

Si l'option / g n'est pas utilisée, m // dans le contexte de la liste renvoie une liste constituée des sous-expressions correspondant aux parenthèses dans le modèle, c'est-à-dire ($ 1, $ 2, $ 3 ...) (Notez qu'ici $ 1 etc. sont également définis). Lorsqu'il n'y a pas de parenthèses dans le modèle, la valeur de retour est la liste (1) pour le succès. Avec ou sans parenthèses, une liste vide est renvoyée en cas d'échec.

Passons à votre code.

my ($id) = _getid("12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n");

say() impose un contexte de liste à ses arguments, vous obtenez donc une liste des captures. Vous n'avez qu'une seule capture, donc la liste a un élément (votre identifiant) et c'est ce qui est imprimé.

my $id = _getid("12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n");

L'affectation à une variable d'échelle est un exemple assez évident de contexte scalaire. Vous obtenez donc le comportement décrit dans le premier extrait de la documentation. Le "1" que vous voyez est la vraie valeur.

[ Mise à jour: Mon explication du problème (tout ce qui est au-dessus de ce point) est bonne. Mais ma solution suggérée (les éléments ci-dessous) n'est pas aussi utile que je le pensais à l'origine. Les autres réponses de TLP et ikegami incluent toutes deux des solutions bien meilleures.]

Pour résoudre ce problème, vous devez imposer un contexte de liste à votre appel de sous-programme. Le moyen le plus simple de le faire est de remplacer votre affectation scalaire par une affectation de liste - en plaçant des parenthèses autour de la variable.

say _getid("12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n");


2 commentaires

Merci, j'aurais dû mentionner que j'ai aussi pensé à cette solution. Cependant, il semble erroné que _getid renvoie une liste en premier lieu. Existe-t-il un moyen de modifier _getid place de manière à ce qu'il renvoie le premier groupe correspondant sous forme de scalaire?


Re " Cependant, il semble erroné que _getid retourne une liste en premier lieu. ", En effet, être forcé d'appeler _getid dans un contexte de liste n'est pas vraiment sympa.



2
votes

Une manière élégante (?) De s'assurer que le sous-programme renvoie toujours un scalaire est d'utiliser un indice sur la liste renvoyée par la correspondance regex:

sub _getid {
    my $str = shift;
    my ($return) = $str =~ /\d+:\d+ \w+ (\d+)/;
    return $return;
}

Bien sûr, tout cela est très «golfique». J'écrirais probablement ce sous-programme plus explicitement, rendant le code réellement lisible pour d'autres personnes:

sub _getid {
    ($_[0] =~ /\d+:\d+ \w+: (\d+)/)[0];    # subscript makes parenthesis return
                                           # 1st element of list
}

Quelques notes sur votre code.

  • Sachez que lorsque vous utilisez $_[0] vous pouvez modifier l'argument par inadvertance, car vous y accédez directement. Une option plus sûre consiste à copier le contenu dans une nouvelle variable à portée lexicale, comme dans mon exemple ci-dessus.

Considérez par exemple sub foo { $_[0]++ } . Si vous exécutez my $foo = 0; foo($foo); print $foo; cela affichera 1 , montrant que $foo été modifié par le sous-programme. Si vous essayez foo(2) vous obtiendrez également l'erreur plutôt étrange Modification of a read-only value attempted .

  • Vous ne devriez probablement pas utiliser de prototypes pour vos sous-programmes. Ils ont une utilisation particulière en Perl, et ce n'est pas ce que la plupart des gens pensent. C'est-à-dire que vous devriez faire sub foo { ... } et non sub foo ($) { ... } . Documentation ici


0 commentaires

-2
votes

Le code fonctionne comme il a été conçu pour fonctionner et non ce que OP attendait.

La première erreur est masquée dans le modèle de correspondance car elle ne tient pas compte : après SomeEvent .

Le résultat de la correspondance dans un contexte scalaire indiquera s'il y a eu correspondance ou non - considérez-le comme une variable booléenne.

Si le modificateur /g est utilisé et que plusieurs correspondances se produisent dans la chaîne, le résultat de la correspondance sera un nombre de correspondances.

Si sur le côté gauche de l'OP de correspondance avait une variable de liste (un tableau), il remplirait le tableau avec des groupes correspondants, mais le code d'origine n'utilise pas cette approche.

Ce que OP doit faire est démontré dans la version modifiée du sous-programme _getid() .

-[]-
-[1]-
_getid returned: 0

Production

use strict;
use warnings;
use feature 'say';

my $str = "12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n";
my $var;

$var = $str =~ /\d+:\d+ \w+ (\d+)/;
say "-[$var]-";

$var = $str =~ /\d+:\d+ \w+: (\d+)/;
say "-[$var]-";

my $id = _getid($str);
say '_getid returned: ' . $id;

sub _getid {
    my $str = shift;
    
    return $1 if $str =~ /\d+:\d+ \w+: (\d+)/;
    
    return undef;
}

Documentation: perlre


1 commentaires

return undef est généralement mieux écrit comme return .



2
votes

Tu as ceci:

# Match in list context returns captures.
# Using a slice, this returns $1, or undef if no match.
sub _getid { ( $_[0] =~ /\d+:\d+ \w+ (\d+)/ )[0] }

Ce qui précède échoue mal si la chaîne ne correspond pas (renvoyant une chaîne "aléatoire"). Les éléments suivants fonctionnent également, mais échouent beaucoup plus en toute sécurité:

# Match in scalar context returns whether the match succeeded or not.
# Returns $1, or undef if no match.
sub _getid { $_[0] =~ /\d+:\d+ \w+ (\d+)/ ? $1 : undef }
sub _getid ($) {
    $_[0] =~ /\d+:\d+ \w+ (\d+)/;
    $1; # or return $1;
}


0 commentaires