2011-09-10 18 views
10

C'è un modo per farlo senza ottenere un ciclo infinito?Loop infinito in perl

while((my $var) = $string =~ /regexline(.+?)end/g) { 
    print $var; 
} 

Questo si traduce in un ciclo infinito, probabilmente perché l'assegnazione di un var direttamente da una regex all'interno mentre restituisce "true" ogni volta?

So che posso fare questo:

while($string =~ /regexline(.+?)end/g) { 
    my $var = $1;  
    print $var; 
} 

ma speravo ho potuto risparmiare una linea. C'è un modificatore regex che posso usare o qualcosa del genere?

(Inoltre, qual è questa notazione/trucco in realtà chiamati, se voglio cercarlo:

(my $var) = $string =~ /regex/; 

Grazie !!

risposta

8

C'è un modo per farlo senza ottenere un loop infinito?

Sì. Utilizzare un foreach() invece di un ciclo while():

foreach my $var ($string =~ /regexline(.+?)end/g) { 

che cosa è questa notazione/trucco in realtà chiamato, se voglio cercarlo

Si chiama una partita in elenca il contesto. È descritto in "perldoc perlop":

Il modificatore g specifica la corrispondenza del modello globale, ovvero la corrispondenza il più volte possibile all'interno della stringa. Il modo in cui si comporta dipende dal contesto. In un contesto di lista ...

+2

Tieni presente che 'foreach' carica l'intero set di risultati in memoria invece di scorrere su di esso come' while' fa. – hhaamu

10

In un contesto scalare, un'espressione regolare con il modificatore /g sarà agire come un iteratore e restituire un valore falso quando non ci sono più corrispondenze:

print "$1\n" while "abacadae" =~ /(a\w)/g;  # produces "ab","ac","ad","ae" 

Con l'assegnazione all'interno del while espressione, y stai valutando la tua espressione regolare nel contesto dell'elenco. Ora la tua espressione regolare non agisce più come un iteratore, ma restituisce solo la lista delle partite. Se la lista non è vuota, restituisce un valore vero:

print "$1\n" while() = "abacadae" =~ /(a\w)/g; # infinite "ae" 

Per risolvere questo problema, si può prendere l'assegnazione di while e utilizzare il $1 variabile built per rendere l'assegnazione all'interno del ciclo?

while ($string =~ /regexline(.+?)end/g) { 
    my $var = $1; 
    print $var; 
} 
0

Ci sono diversi modi per farlo con meno codice.

Diciamo che si dispone di un file chiamato lines.txt:

regexlineabcdefend 
regexlineghijkend 
regexlinelmnopend 
regexlineqrstuend 
This line does not match 
Neither does this 
regexlinevwxyzend 

e si desidera estrarre i pezzi che corrispondono alla vostra regex, cioè pezzi della linea tra "regexline" e "fine". Uno script Perl semplice è:

while (<STDIN>) { 
    print "$1\n" if $_ =~ /regexline(.+?)end/ 
} 

quando viene eseguito come questo

$ perl match.pl < lines.txt 

si ottiene

abcdef 
ghijk 
lmnop 
qrstu 
vwxyz 

Si può anche fare il tutto sulla riga di comando!

$ perl -nle 'print $ 1 se $ _ = ~ /regexline(.+?)end/' < lines.txt abcdef ghijk lmnop qrstu vwxyz

Per quanto riguarda la seconda domanda va, non sono sicuro che lo sia uno speciale nome Perl per quel trucco.

0

Penso che la soluzione migliore sia sostituire semplicemente la stringa $ all'interno del ciclo ...così:

while((my $var) = $string =~ /regexline(.+?)end/g) { 
    $string =~ s/$var//; 
    print $var . "\n"; 
} 
+0

Ti suggerisco di provare a eseguire quel codice; ha errori di sintassi. 'stampa $ var. "\ n"; 'è ok, ma' print "$ var \ n"; 'è più pulito. E non è necessario fare un'altra sostituzione su '$ stringa'; puoi catturare il nuovo valore nella regexp iniziale e assegnarlo a '$ stringa'. –

8

Il Perl regular expressions tutorial detto:

In contesto scalare, successive invocazioni contro una stringa avranno // g salti da match a match, tenendo traccia della posizione nella stringa raggiunta lungo .

Ma:

In un contesto di lista, // g restituisce un elenco di gruppi abbinati, o se non ci sono raggruppamenti, un elenco di match all'intera espressione regolare.

Vale a dire, in un contesto di lista //g restituisce un array di tutte le partite catturati in una sola volta (di cui in seguito si scarta tutti tranne il primo), e poi fa tutto da capo ogni volta che l'esegue ciclo (vale a dire per sempre).

Quindi non è possibile utilizzare l'assegnazione del contesto dell'elenco in una condizione di loop, perché non fa ciò che si desidera.

Se ti ostini a usare il contesto di lista, si potrebbe fare questo, invece:

foreach my $var ($string =~ /regexline(.+?)end/g) { 
    print $var; 
} 
0

Non so cosa si intende fare con questa stampa, ma questo è un bel modo di farlo:

say for $string =~ /regex(.+?)end/g; 

Il per (lo stesso del foreach) espande la corrispondenza regex in un elenco dei gruppi di cattura e li stampa. Funziona così:

@matches = $string =~ /regex(.+?)end/g; 
say for (@matches); 

while è un po 'diversa. Poiché utilizza un contesto scalare, non carica i gruppi di cattura in memoria.

say $1 while $string =~ /regex(.+?)end/g; 

Farà qualcosa come il tuo codice originale, tranne che non abbiamo bisogno di usare una variabile di transizione $var, ci basta stampare subito.

1

Questa è l'unica circostanza in cui non è possibile evitare di utilizzare le vars globali senza modificare il comportamento.

while ($string =~ /regexline(.+?)end/g) { 
    my $var = $1; 
    ... 
} 

Se si dispone di una sola acquisizione, è possibile evitare di utilizzare le vars globali trovando tutte le corrispondenze contemporaneamente.

for my $var ($string =~ /regexline(.+?)end/g) { 
    ... 
} 

Il costo aggiuntivo della seconda versione è solitamente trascurabile.