2009-10-21 13 views
9

Dire, ho una riga che contiene la seguente stringa:Perché la mia regex Perl non golosa corrisponde ancora troppo?

 
"$tom" said blah blah blash. "$dick" said "blah blah blah". "$harry" said blah blah blah. 

e voglio estrarre

 
"$dick" said "blah blah blah" 

Ho il seguente codice:

my ($term) = /(".+?" said ".+?")/g; 
print $term; 

ma mi dà più di quello che mi serve:

 
"$tom" said blah blah blash. "$dick" said "blah blah blah" 

ho provato raggruppamento il mio modello nel suo insieme utilizzando le parentesi non-cattura:

my ($term) = /((?:".+?" said ".+?"))/g; 

Ma il problema persiste.

Ho riletto la sezione Quantificatori di Nongreedy di Learning Perl ma non mi ha portato da nessuna parte finora.

Grazie per qualsiasi guida che generosamente offrono :)

+1

Il primo "bla bla blash" non è tra virgolette, quindi la tua espressione regolare è afferrare il secondo set. – Ether

+0

@Ether, il mio problema è: ho pensato che Perl potesse trattare il mio pattern nel suo insieme. Ma mi sbagliavo. Mi stanno rendendo più chiaro che Perl cerca sempre di abbinare il primo sub pattern e poi il subpattern successivo e così via. Sembra che non esista "l'abbinamento di un intero modello simultaneo". – Mike

+1

@brian, grazie. Mi piace il modo in cui hai riformulato la mia domanda :) – Mike

risposta

18

Il problema è che, anche se non è avido, mantiene ancora cercando. L'espressione regolare non vede

"$tom" said blah blah blash. 

e pensare: "Oh, la roba seguendo il 'detto' non è citato, quindi salterò quello." Pensa "bene, la roba dopo" ha detto "non è quotata, quindi deve ancora far parte della nostra citazione." Così ".+?" corrisponde

"$tom" said blah blah blash. "$dick" 

quello che vuoi è "[^"]+". Questo corrisponderà a due virgolette che racchiudono tutto ciò che non è un segno di virgoletta. Quindi la soluzione finale:

("[^"]+" said "[^"]+") 
+0

Grazie Chris, per la soluzione e la spiegazione :) Grazie mille per l'ingombro! – Mike

+1

++! Un altro buon motivo per usare '[^"] + 'è che ridurrà il back-tracking non necessario e renderà più efficiente la regex – daotoad

+1

@daotoad - Ho discusso di questo con Alex Martelli nella sua risposta, che viene spesso citato, ma anche per 10.000.000 confronti la differenza di prestazioni è ancora quasi impercettibile, sia in Perl che in Python. Sembra che il mantra cambi da "il compilatore lo ottimizzerà" a "il motore regex lo ottimizzerà": P (Vedi il mio commento al suo risposta per i risultati dei miei tempi.) –

3

Purtroppo " è un carattere particolare, abbastanza per bisogno di essere trattati con cura. Uso:

my ($term) = /("[^"]+?" said "[^"]+?")/g; 

e dovrebbe funzionare correttamente (lo fa per me ...!). Cioè corrispondere esplicitamente alle sequenze di "nondoublequotes" piuttosto che alle sequenze di caratteri arbitrari.

+1

Woah! Il Signore di Python risponde a una domanda Perl! Pizzicami, sto sognando. (Come nota, la partita non greedy non è più necessaria, e probabilmente rallenterà il regex engine, ma funziona, come dici tu.) –

+0

Sono stato un eccellente Perl'er di nuovo in Perl 4 volte (e questa domanda è piuttosto pertinente a Perl 4 ;-) - sono le strane novità di Perl 5 che mi hanno buttato fuori e alla fine mi ha fatto diventare un Pythonista! -) (Tornato in "Perl 4" volte, non-goloso non è mai stato meno efficiente che avido - non ho guardato i motori di _current_ Perl RE per vedere se hanno rovinato anche quelli, però! -). –

+0

Grazie, Alex :) – Mike

3

Altri hanno menzionato come risolvere questo problema.

risponderò come è possibile eseguire il debug di questo: è possibile vedere ciò che sta accadendo utilizzando più Cattura:

bash$ cat story | perl -nle 'my ($term1, $term2, $term3) = /(".+?") (said) (".+?")/g ; 
     print "term1 = \"$term1\" term2 = \"$term2\" term3 = \"$term3\" \n"; ' 
term1 = ""$tom" said blah blah blash. "$dick"" term2 = "said" term3 = ""blah blah blah"" 
2

Il tuo problema è che ci sono due possibili corrispondenze per il tuo regexp, quello che si desidera (uno più corto) e quello scelto dal motore regex. Il motore sceglie quella specifica partita perché preferisce una partita che inizia prima nella stringa ed è più lunga per una partita che inizia più tardi ed è più breve. In altre parole, le partite iniziali vincono su quelle più brevi.

Per risolvere questo è necessario rendere la tua espressione regolare più specifica (come nel dire al motore che $ term non deve contenere virgolette. È una buona idea rendere comunque le tue espressioni regolari il più specifiche possibile.

Per maggiori dettagli e trucchi per quanto riguarda le espressioni regolari, mi consiglia il libro di Jeffrey Friedl: Mastering Regular Expressions

+0

@kixx, grazie per la spiegazione e la raccomandazione del libro. – Mike

Problemi correlati