Regular Expressions
Quantificatori avidi e pigri
Ricerca…
Parametri
quantificatori | Descrizione |
---|---|
? | Abbina il carattere precedente o la sottoespressione 0 o 1 volte (preferibilmente 1). |
* | Abbina il carattere precedente o la sottoespressione 0 o più volte (il più possibile). |
+ | Abbina il precedente carattere o sottoespressione 1 o più volte (il maggior numero possibile). |
{n} | Abbina il carattere o la sottoespressione precedente esattamente n volte. |
{min,} | Far corrispondere il carattere precedente o sottoespressione min o più volte (il maggior numero possibile). |
{0,max} | Abbina il carattere o la sottoespressione precedente al massimo o al massimo (il più vicino possibile al massimo ). |
{min,max} | Abbina il carattere o la sottoespressione precedente almeno min volte ma non più di max volte (il più vicino possibile al massimo ). |
Quantificatori pigri | Descrizione |
?? | Abbina il carattere precedente o la sottoespressione 0 o 1 volte (preferibilmente 0). |
*? | Abbina il carattere precedente o la sottoespressione 0 o più volte (il meno possibile). |
+? | Abbina il carattere precedente o la sottoespressione 1 o più volte (il meno possibile). |
{n}? | Abbina il carattere o la sottoespressione precedente esattamente n volte. Nessuna differenza tra la versione avida e pigra. |
{min,}? | Corrisponde al carattere o espressione che precede min o più volte (il più vicino a min possibile). |
{0,max}? | Abbina il carattere o la sottoespressione precedente al massimo o al massimo (il meno possibile). |
{min,max}? | Abbina il carattere o la sottoespressione precedente almeno min volte ma non più di max volte (il più vicino possibile al minimo ). |
Osservazioni
golosità
Un quantificatore avido tenta sempre di ripetere il sub-pattern il più volte possibile prima di esplorare le corrispondenze più brevi con il backtracking.
Generalmente, un modello goloso corrisponderà alla stringa più lunga possibile.
Per impostazione predefinita, tutti i quantificatori sono golosi.
Pigrizia
Un pigro (chiamato anche non avido o riluttante) quantificatore tenta sempre di ripetere il sub-pattern come meno possibile, prima di esplorare corrisponde più dall'espansione.
Generalmente, un modello pigro corrisponderà alla stringa più breve possibile.
Per rendere i quantificatori pigri, basta aggiungere ?
al quantificatore esistente, ad esempio +?
, {0,5}?
.
Il concetto di avidità e pigrizia esiste solo nei motori di backtracking
La nozione di quantificatore avido / pigro esiste solo nei motori regex di backtracking. Nei motori regex non di retromarcia o nei motori regex conformi a POSIX, i quantificatori specificano solo il limite superiore e il limite inferiore della ripetizione, senza specificare come trovare la corrispondenza - quei motori corrisponderanno sempre alla stringa più lunga a sinistra, indipendentemente.
Avidità contro pigrizia
Dato il seguente input:
aaaaaAlazyZgreeedyAlaaazyZaaaaa
Useremo due schemi: uno avido: A.*Z
e uno pigro: A.*?Z
Questi modelli producono le seguenti corrispondenze:
-
A.*Z
produce 1 partita:AlazyZgreeedyAlaaazyZ
(esempi: Regex101 , Rubulare ) -
A.*?Z
produce 2 partite:AlazyZ
eAlaaazyZ
(esempi: Regex101 , Rubulare )
Innanzitutto concentrati su ciò che fa A.*Z
Quando ha abbinato il primo A
, il .*
, Essendo avidi, cerca di abbinarne altrettanti .
il più possibile
aaaaaAlazyZgreeedyAlaaazyZaaaaa
\________________________/
A.* matched, Z can't match
Poiché la Z
non corrisponde, i backtrack del motore e .*
devono quindi corrispondere ad un numero inferiore .
:
aaaaaAlazyZgreeedyAlaaazyZaaaaa
\_______________________/
A.* matched, Z can't match
Succede qualche altra volta, finché non arriva a questo:
aaaaaAlazyZgreeedyAlaaazyZaaaaa
\__________________/
A.* matched, Z can now match
Ora Z
può corrispondere, quindi il modello generale corrisponde:
aaaaaAlazyZgreeedyAlaaazyZaaaaa
\___________________/
A.*Z matched
Al contrario, la riluttante (pigra) ripetizione in A.*?Z
corrisponde per prima a pochi .
come possibile, e poi prendendo di più .
come necessario. Questo spiega perché trova due corrispondenze nell'input.
Ecco una rappresentazione visiva di ciò che i due modelli corrispondono:
aaaaaAlazyZgreeedyAlaaazyZaaaaa
\____/l \______/l l = lazy
\_________g_________/ g = greedy
Esempio basato sulla risposta fatta da poligenelubrificanti .
Lo standard POSIX non include il ?
operatore, così tanti motori regex POSIX non hanno una corrispondenza lenta. Sebbene il refactoring, in particolare con il "trucco più grande di sempre" , possa in alcuni casi essere d'aiuto, l'unico modo per avere una vera corrispondenza lazy è usare un motore che lo supporta.
Confini con più corrispondenze
Quando hai un input con limiti ben definiti e ti aspetti più di una corrispondenza nella stringa, hai due opzioni:
- Usando quantificatori pigri;
- Utilizzando una classe di caratteri negata.
Considera quanto segue:
Hai un semplice motore di template, vuoi sostituire sottostringhe come $[foo]
dove foo
può essere qualsiasi stringa. Vuoi sostituire questa sottostringa con qualsiasi cosa basata sulla parte tra []
.
Puoi provare qualcosa come \$\[(.*)\]
, E quindi usare il primo gruppo di cattura.
Il problema è che se hai una stringa come something $[foo] lalala $[bar] something else
tua partita sarà
something $[foo] lalala $[bar] something else
| \______CG1______/|
\_______Match______/
Il gruppo di cattura è foo] lalala $[bar
che può o non può essere valida.
Hai due soluzioni
Usando la pigrizia: in questo caso fare
*
pigro è un modo per andare a trovare le cose giuste. Quindi cambi la tua espressione in\$\[(.*?)\]
Usando la classe di caratteri negata:
[^\]]
cambi la tua espressione in\$\[([^\]]*)\]
.
In entrambe le soluzioni, il risultato sarà lo stesso:
something $[foo] lalala $[bar] something else
| \_/| | \_/|
\____/ \____/
Con il gruppo di cattura che è rispettivamente foo
e bar
.
L'uso della classe di caratteri negata riduce il problema del backtracking e può far risparmiare molto tempo alla CPU quando si tratta di input di grandi dimensioni.