2010-11-23 4 views
4

Quand je lance ce code:captures nommées qui correspondent à plus d'une fois (Perl)

$_='xaxbxc'; 
if(/(x(?<foo>.))+/) { 
    say "&: ", $&; 
    say "0: ", $-{foo}[0]; 
    say "1: ", $-{foo}[1]; 
} 

-je obtenir:

&: xaxbxc 
0: c 
1: 

Je comprends que cela est la façon dont il est censé fonctionner, mais je tiens à être en mesure d'obtenir en quelque sorte la liste de tous les matches ('a', 'b', 'c') au lieu de seulement le dernier match (c). Comment puis-je faire ceci?

+0

similaires à [cette question] (http://stackoverflow.com/questions/3459721/regex-group-in-perl-how-to-capture-elements-into-array-from-regex-group-that-mat) – Cameron

Répondre

3

Dans des situations comme celles-ci, en utilisant des blocs de code embeded fournit un moyen facile:

my @match; 
$_='xaxbxc'; 
if(/((?:x(.)(?{push @match, $^N}))+)/) { 
    say "\$1: ", $1; 
    say "@match" 
} 

qui imprime:

$1: xaxbxc 
a b c 
+0

Ooh, cool! Je ne savais même pas que ça existait :-) – Cameron

+0

Ouais, j'ai fini par utiliser ça comme ma solution ... mais le backtracking est compliqué ... J'ai dû déplacer mon bloc de code plus loin de la capture pour être sûr n'a pas été appelé pendant le backtracking – JoelFan

4

Je ne pense pas qu'il y ait une façon de le faire en général (s'il vous plaît me corriger si je me trompe), mais il est susceptible d'être un moyen d'atteindre le même objectif final dans des situations spécifiques. Par exemple, cela fonctionnerait pour votre exemple de code spécifique:

$_='xaxbxc'; 
while (/x(?<foo>.)/g) { 
    say "foo: ", $+{foo}; 
} 

Qu'est-ce que vous essayez exactement d'accomplir? Nous pourrions peut-être trouver une solution à votre problème même s'il n'y a aucun moyen de faire des captures répétées.

3

Perl permet une expression régulière pour correspondre à plusieurs reprises avec le commutateur « g » après la fin. Chaque match individuel peut alors être mis en boucle sur, comme décrit dans le paragraphe mondial de correspondance du Using Regular Expressions in Perl section of the Perl Regex Tutorial:

while(/(x(?<foo>.))+/g){ 
    say "&: ", $&; 
    say "foo: ", $+{foo}; 
} 

Cela produira une liste itéré:

&: xa 
foo: a 
&: xb 
foo: b 
&: xc 
foo: c 

qui est toujours pas ce que vous voulez, mais c'est vraiment proche. La combinaison d'une regex globale (/ g) avec votre ancienne regex locale le fera probablement. Généralement, créez un groupe de capture autour de votre groupe répété, puis ré-analysez juste ce groupe avec une expression rationnelle globale qui représente juste une seule itération de ce groupe, et parcourez-la ou utilisez-la comme une liste.

Cela ressemble à une question assez similaire à celle-ci - au moins en réponse, sinon forumlation - a été répondu par quelqu'un de beaucoup plus compétent chez Perl que moi: "Is there a Perl equivalent of Python's re.findall/re.finditer (iterative regex results)?" Vous pourriez vouloir vérifier les réponses pour cela aussi, avec plus de détails sur l'utilisation correcte des regexes globales. (Perl n'est pas mon langage, j'ai juste une appréciation malsaine pour les expressions régulières.)

0

Je ne suis pas sûr que ce soit exactement ce que vous cherchez, mais le code suivant devrait faire l'affaire.

$_='xaxbxc'; 
@l = /x(?<foo>.)/g; 

print join(", ", @l)."\n"; 

Mais, je ne suis pas sûr que cela fonctionnerait avec des chaînes se chevauchant.

1

La variable %- est utilisée lorsque vous avez plus d'un groupe nommé dans le même modèle, et non quand un groupe donné est itéré.

C'est pourquoi /(.)+/ ne charge pas $1 avec chaque caractère distinct, juste avec le dernier. Idem avec /(<x>.)+/. Cependant, avec /(<x>.)(<x>.)/ vous avez deux groupes différents, donc <x>$-{x}.Considérez:

% perl -le '"foobar" =~ /(?<x>.)(?<x>.)/; print "x#1 is $-{x}[0], x#2 is $-{x}[1]"' 
x#1 is f, x#2 is o 

% perl -le '"foobar" =~ /(?:(?<x>.)(?<x>.))+/; print "x#1 is $-{x}[0], x#2 is $-{x}[1]"' 
x#1 is a, x#2 is r 
Questions connexes