2010-11-23 6 views
4

Wenn ich diesen Code ausführen:genannt Aufnahmen, die mehr als einmal (Perl) entsprechen

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

ich:

&: xaxbxc 
0: c 
1: 

Ich verstehe, dass dies ist, wie es funktionieren soll, aber ich würde mag es irgendwie die Liste aller Spiele zu bekommen ('a', 'b', 'c') statt nur das letzte Spiel (c). Wie kann ich das machen?

+0

Ähnlich [diese Frage] (http://stackoverflow.com/questions/3459721/regex-group-in-perl-how-to-capture-elements-into-array-from-regex-group-that-mat) – Cameron

Antwort

3

In Situationen wie diesen, embeded Codeblöcke mit bietet eine einfache Möglichkeit, aus:

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

die druckt:

$1: xaxbxc 
a b c 
+0

Ooh, cool! Ich wusste nicht einmal, dass das existierte :-) – Cameron

+0

Ja, ich benutze dies als meine Lösung ... aber das Backtracking-Problem ist schwierig ... Ich musste meinen Code-Block weiter weg von der Erfassung bewegen, um es sicherzustellen wurde während des Backtrackings nicht aufgerufen – JoelFan

4

Ich glaube nicht, dass es einen Weg gibt, dies im Allgemeinen zu tun (bitte korrigiere mich, wenn ich falsch liege), aber es gibt wahrscheinlich einen Weg, das gleiche Ziel in bestimmten Situationen zu erreichen. Dies würde beispielsweise für Ihr spezifisches Codebeispiel funktionieren:

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

Was genau möchten Sie erreichen? Vielleicht könnten wir eine Lösung für Ihr tatsächliches Problem finden, selbst wenn es keine Möglichkeit gibt, wiederholte Aufnahmen zu machen.

3

Perl erlaubt einem regulären Ausdruck mehrere Male mit dem "g" -Schalter nach dem Ende übereinzustimmen. Jedes einzelne Spiel kann dann geschlungen über, wie im Global Matching Unterabschnitt der Using Regular Expressions in Perl section of the Perl Regex Tutorial beschrieben:

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

Dies wird eine Liste iteriert produzieren:

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

, die noch nicht das, was Sie wollen, aber es ist sehr nah. Das Kombinieren einer globalen Regex (/ g) mit Ihrer vorherigen lokalen Regex wird es wahrscheinlich tun. Erstellen Sie im Allgemeinen eine erfassende Gruppe um Ihre wiederholte Gruppe und parsen Sie dann nur diese Gruppe mit einem globalen Regex, der nur eine einzige Iteration dieser Gruppe darstellt, und durchlaufen Sie sie oder verwenden Sie sie als Liste.

Es sieht aus wie eine Frage ziemlich ähnlich zu diesem - zumindest in der Antwort, wenn nicht Forumierung - wurde von jemand viel kompetenter beantwortet in Perl als ich: "Is there a Perl equivalent of Python's re.findall/re.finditer (iterative regex results)?" Sie möchten vielleicht die Antworten dafür auch überprüfen, mit mehr Details über den richtigen Gebrauch von globalen Regexen. (Perl ist nicht meine Sprache, ich habe nur eine ungesunde Wertschätzung für reguläre Ausdrücke.)

0

Ich bin mir nicht sicher, dass genau das ist, was Sie suchen, aber der folgende Code sollte den Trick tun.

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

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

Aber ich bin mir nicht sicher, dass dies mit überlappenden Strings funktionieren würde.

1

Die Variable %- wird verwendet, wenn Sie mehr als eine derselben benannten Gruppe im selben Muster haben, nicht wenn die gegebene Gruppe zufällig iteriert wird.

Deshalb /(.)+/ lädt nicht $1 mit jedem einzelnen Zeichen, nur mit dem letzten. Gleiches mit /(<x>.)+/. Mit /(<x>.)(<x>.)/ haben Sie jedoch zwei verschiedene <x> Gruppen, also $-{x}.Bedenken Sie:

% 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 
Verwandte Themen