La maggior parte dei bug nelle espressioni regolari non sono errori di sintassi. Un pattern può essere perfettamente valido ed essere comunque sbagliato: cattura troppo, cattura troppo poco o — peggio — blocca la pagina quando incontra una stringa ostile. Tra un pattern che funziona con gli esempi in testa e uno che funziona con dati reali ci sono i test.
Questo articolo descrive un workflow pratico per scrivere, testare e fare il debug di espressioni regolari nel browser, più i fallimenti che mordono più spesso.
Testa con dati reali, non con esempi di prova
Le stringhe di prova adulano un pattern. Un input reale contiene a capo dimenticati, spazi finali, emoji, valori vuoti e quaranta kilobyte di markup copiato.
- Incolla il campione reale peggiore che hai — idealmente quello che ha già rotto qualcosa.
- Includi la stringa vuota, un singolo carattere e una riga molto lunga.
- Includi testo non ASCII se l’input può contenerlo:
Ü,日本語,🙂. - Conta i match, non solo il primo. I quantificatori con errore di uno e le sovrapposizioni golose compaiono solo qui.
Testare con evidenziazione in tempo reale rende l’operazione economica: ogni posizione di match si aggiorna mentre modifichi il pattern, invece di riemergere tre giorni dopo in un file di log.
I flag cambiano il significato di un pattern
La stessa espressione si comporta diversamente a seconda dei flag con cui viene eseguita:
| Flag | Effetto | Sorpresa tipica |
|---|---|---|
i |
ignora maiuscole e minuscole | cattura anche identificatori che volevi distinguere |
g |
globale — trova tutti i match | in JavaScript, test() e exec() mantengono stato tra le chiamate |
m |
multilinea — ^ e $ per riga |
^ non significa più «inizio stringa» |
s |
il punto cattura anche i ritorni a capo | .* inizia a superare i confini di riga |
u |
modalità Unicode | necessario per gli escape a punto di codice |
Il flag g di JavaScript merita una menzione: con esso regex.test() incrementa lastIndex, per cui chiamate ripetute sullo stesso oggetto restituiscono risposte diverse. È la fonte classica del bug «funziona una volta, fallisce al secondo tentativo».
Gruppi di cattura e gruppi con nome
I gruppi estraggono dati, ma costano anche memoria e rendono il pattern più difficile da leggere.
// i gruppi con nome sopravvivono al riordinamento, quelli numerati no
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const m = "2026-09-22".match(re);
m.groups.year; // "2026"
m.groups.month; // "09"
Preferisci i gruppi con nome a quelli numerati, così riordinare il pattern non sposta silenziosamente i tuoi riferimenti. Se hai bisogno di parentesi solo per raggruppare e non per catturare, usa un gruppo non catturante: (?:...).
Backtracking catastrofico
Quantificatori annidati come (a+)+$ o (\s*)* possono mandare un motore in tempo esponenziale su stringhe che quasi corrispondono. Il pattern è istantaneo su input amichevoli e si blocca su un input ostile — ed è esattamente quello che invia un attaccante.
- Evita quantificatori su gruppi che ne contengono già.
- Ancora dove puoi.
^e$consentono al motore di rifiutare presto. - Sottoponi a una lunga stringa quasi-corrispondente. Se il tempo di match cresce con ogni carattere aggiunto, il pattern non è sicuro.
- Valuta un motore a tempo lineare (stile RE2) per input non affidabili lato server.
Mantieni i pattern leggibili
- Dai un nome al pattern nel codice —
const ISO_DATE = /.../batte un letterale anonimo ripetuto in sei file. - Usa la modalità a spazi liberi (
x) se il motore la supporta, per aggiungere commenti e spazi dentro il pattern. - Sii esplicito sulle cifre.
[0-9]significa cifre ASCII;\dcorrisponde anche ad altri sistemi di scrittura in modalità Unicode. - Lascia due esempi con il pattern: uno che deve corrispondere e uno che non deve.
Un workflow pratico
| Passo | Cosa controlli |
|---|---|
| 1. Scrivi il pattern più piccolo che corrisponde a un campione reale | l’evidenziazione cade dove ti aspetti |
| 2. Aggiungi campioni negativi | nessun match sugli input che devono fallire |
| 3. Attiva i flag deliberatamente | ogni flag c’è per un motivo |
| 4. Sottoponi il pattern a input lunghi e ostili | il match appare istantaneamente |
| 5. Converti in codice con gruppi con nome | nessun riferimento posizionale residuo |
Provalo
Apri un tester di regex, incolla il tuo pattern e un campione reale e osserva ogni match evidenziarsi mentre scrivi. Poiché il test gira nel tuo browser, il testo che stai validando non lascia mai il tuo dispositivo — e conta non appena il campione contiene un token, una chiave o dati di clienti.