CodeKitHub
Italiano
Corrispondenza “greedy” e “lazy” nelle espressioni regolari: perché il tuo pattern cattura troppo

Corrispondenza “greedy” e “lazy” nelle espressioni regolari: perché il tuo pattern cattura troppo

Pubblicato il 24 lug 2026

Si scrive <b>.*</b> per estrarre il contenuto di un tag in grassetto, lo si prova su <b>Hello</b> e funziona perfettamente. Poi lo esegui su codice HTML reale con due tag di grassetto sulla stessa riga — <b>Hello</b> and <b>World</b> — e invece di due corrispondenze, ottieni un’unica corrispondenza gigante che copre entrambi i tag. Non si tratta di un bug del tuo motore di espressioni regolari; è il comportamento predefinito di *, + e {n,m}, e basta un solo carattere per risolvere il problema una volta capito cosa sta succedendo.

Perché .* cattura più di quanto ti aspetti

Per impostazione predefinita, i quantificatori nelle espressioni regolari sono avidi.* non significa “trova alcuni caratteri”, ma “trova il maggior numero possibile di caratteri, poi riduci la ricerca solo se il resto del pattern lo richiede assolutamente”.

Esaminiamo passo dopo passo <b>.*</b> rispetto a <b>Hello</b> and <b>World</b>:

  1. <b> trova corrispondenza con il primo <b>.
  2. .* inizia consumando l’intera parte restante della stringa — tutto fino alla fine, compreso il secondo <b>World</b>.
  3. Il motore deve quindi trovare </b> per completare la corrispondenza, quindi inizia a risalire dalla fine di .* un carattere alla volta.
  4. Il primo punto (scorrendo all’indietro dalla fine) in cui </b> si inserisce è proprio l’ultimo </b> della stringa — non il primo in cui “logicamente” dovrebbe fermarsi.

Pertanto, la corrispondenza finisce per essere <b>Hello</b> and <b>World</b> nella sua interezza, poiché la corrispondenza avida prova sempre prima la stringa più lunga possibile e la riduce solo quanto basta per far sì che il resto del pattern abbia successo.

La soluzione: rendere il quantificatore pigro con ?

Aggiungendo un ? immediatamente dopo un quantificatore, lo si trasforma da avido a pigro (chiamato anche “non avido” o “riluttante”): *?, +?, ??, {n,m}?.

Un quantificatore pigro fa l’opposto: inizia abbinando il minor numero possibile di caratteri, per poi espandersi solo se il resto del pattern non può ancora essere risolto.

<b>.*?</b> sulla stessa stringa:

  1. <b> corrisponde al primo <b>.
  2. .*? inizia a trovare corrispondenze con zero caratteri.
  3. Il motore verifica: </b> corrisponde proprio qui? No (ci troviamo a “Hello…”, non ancora a un </b>) — quindi espande .*? di esattamente un carattere e verifica nuovamente.
  4. Questo processo si ripete carattere per carattere finché .*? non ha consumato esattamente Hello, a quel punto </b> viene trovato immediatamente.

Risultato: <b>Hello</b> e <b>World</b> vengono restituiti come due corrispondenze separate, il che è quasi sempre ciò che si desidera effettivamente quando si analizzano strutture simili a tag o a delimitatori.

Quando si desidera effettivamente l’approccio avido (non è solo “l’impostazione predefinita sbagliata”)

L’approccio avido non è un errore del linguaggio: è corretto per un caso diverso, altrettanto comune: la corrispondenza con il confine più esterno di qualcosa, non con l’unità più piccola al suo interno. Se stai estraendo «tutto ciò che si trova tra il primo { e l’ultimo } » in un blocco di testo simile a JSON (ad esempio, per estrarre un intero oggetto indipendentemente dal livello di nidificazione), l’approccio “greedy” è esattamente quello giusto, mentre quello “lazy” si fermerebbe invece al primo } interno, fornendoti un frammento troncato e non valido.

Regola generale: “lazy” per piccoli blocchi delimitati ripetuti (tag, stringhe tra virgolette, elementi di elenco); “greedy” per “catturare l’intero intervallo esterno”.

Riferimento rapido

Modello Comportamento Da usare quando
.*, .+, {n,m} Greedy — trova la corrispondenza più lunga possibile Si desidera lo span più esterno, oppure c’è una sola corrispondenza nella stringa
.*?, .+?, {n,m}? Lazy — trova la corrispondenza più breve possibile Si hanno più blocchi delimitati simili e si desidera estrarre ciascuno separatamente

Se non sei sicuro di quale modalità stia effettivamente utilizzando un pattern sul tuo input reale anziché sulla stringa di prova, eseguirlo sul testo effettivo con corrispondenze multiple in un regex tester con l’evidenziazione delle corrispondenze in tempo reale rende immediatamente visibile la differenza: vedrai un unico grande blocco evidenziato per la modalità avida contro diversi blocchi separati per quella pigra, il che di solito è più veloce che analizzarlo carattere per carattere.

← Torna al blog