Espressioni regolari: classi, quantificatori e gruppi
Un’espressione regolare, o regex, è un piccolo pattern che descrive del testo: «quattro cifre, un trattino, due cifre», oppure «una parola che inizia con la maiuscola». I programmi le usano per cercare, per controllare un input e per estrarre parti di una riga. Questa guida spiega i pezzi di un pattern, con esempi da incollare nel tester di regex, e poi le trappole: pattern che trovano più del previsto, pattern che possono non finire mai e le differenze tra i motori di JavaScript, Python e PHP. Gli esempi usano la sintassi di JavaScript, quella del tester e di tutti i browser.
Caratteri che significano altro
Quasi tutti i caratteri di un pattern rappresentano sé stessi: sole trova le lettere s, o, l ed e in quest’ordine. Quattordici no, perché nel pattern hanno un compito: . ^ $ * + ? ( ) [ ] { } | \ e, nel codice JavaScript, la barra che chiude il pattern. Per cercarne uno così com’è, mettici davanti una barra rovesciata.
1.5Trova 1.5, ma anche 1x5 e 1-5: il punto sta per un carattere qualsiasi.1\.5Trova solo 1.5: la barra rovesciata rende il punto un punto.\$\d+Trova $30: un simbolo del dollaro con escape, poi cifre.
Classi di caratteri
Una classe trova un carattere di un insieme. Le parentesi quadre elencano l’insieme; una barra rovesciata con una lettera ne dà uno già pronto; la lettera maiuscola significa il contrario.
| Pattern | Trova un… |
|---|---|
[aeiou] | vocale senza accento: a, e, i, o o u |
[^aeiou] | carattere che non sia uno di quelli, spazi e cifre compresi |
[a-z0-9] | lettera minuscola dalla a alla z, o una cifra |
\d | cifra da 0 a 9 (\D: tutto il resto) |
\w | lettera dalla A alla Z o dalla a alla z, cifra o trattino basso (\W: tutto il resto) |
\s | spazio, tabulazione, a capo o altro spazio bianco (\S: tutto il resto) |
. | carattere tranne l’a capo (qualsiasi carattere con il flag s) |
\p{L} | lettera di qualunque alfabeto, con il flag u o v |
In JavaScript, \d e \w conoscono solo l’alfabeto latino di base: \w+ su città trova solo citt, e \d non trova la cifra araba orientale ٣. Con il flag u, \p{L}+ trova città intera e \p{Nd} trova le cifre di qualunque scrittura. Python 3 fa il contrario: sul testo normale, il suo \d e il suo \w coprono già Unicode, salvo con il flag ASCII. Il motore PCRE2 di PHP resta all’ASCII finché le proprietà Unicode non vengono attivate, per esempio con (*UCP) all’inizio del pattern.
Quantificatori: quante volte
Un quantificatore dice quante volte può ripetersi l’elemento che lo precede. Si applica a un carattere, a una classe o a un gruppo tra parentesi.
| Pattern | Significa |
|---|---|
a* | zero o più |
a+ | uno o più |
a? | zero o uno, cioè facoltativo |
a{3} | esattamente tre |
a{3,} | tre o più |
a{2,4} | da due a quattro |
I quantificatori sono avidi: prendono tutto ciò che possono e restituiscono solo quello che serve al resto del pattern. Su <b>grassetto</b>, il pattern <.+> trova l’intera stringa, dal primo < all’ultimo >. Un punto interrogativo dopo lo rende pigro, che prende il meno possibile: <.+?> trova <b> e poi </b>. colou?r trova entrambe le grafie inglesi, color e colour, e \d{3,} trova i numeri di tre cifre o più.
Ancore e confini di parola
Le ancore trovano una posizione, non un carattere. ^ è l’inizio del testo e $ la fine; con il flag m diventano l’inizio e la fine di ogni riga. \b è un confine di parola, il punto tra un carattere di parola e qualcos’altro, quindi \bsole\b trova sole come parola ma non dentro solenne. Senza ancore, un pattern usato per validare accetta anche testo con caratteri in più intorno alla parte che corrisponde: \d{4} trova quattro cifre dentro abc12345, mentre ^\d{4}$ accetta solo esattamente quattro cifre.
\bsole\bSu «sole solenne sole.»: due corrispondenze, le due parole sole.^\d{4}$Accetta 2026; rifiuta 12026 e 2026a.
Gruppi, alternative e riferimenti all’indietro
Le parentesi raggruppano parte di un pattern perché un quantificatore o un’alternativa valga per tutto il gruppo, e catturano il testo trovato perché un programma possa leggerlo: (\d{4})-(\d{2})-(\d{2}) cattura separatamente anno, mese e giorno. (?:…) raggruppa senza catturare, e (?<anno>\d{4}) dà un nome alla cattura.
La barra verticale significa «oppure» ed è ciò che ha la precedenza più bassa: ^sole|mare$ significa «sole all’inizio» oppure «mare alla fine», quindi trova sole in solenne e mare in amare. Per dire «tutto il testo è sole o mare», raggruppa le alternative: ^(?:sole|mare)$.
Un riferimento all’indietro trova lo stesso testo che un gruppo ha già catturato: (\w)\1 trova una lettera seguita da sé stessa, pp e cc in cappuccino, e \k<nome> fa lo stesso per un gruppo con nome.
Lookahead e lookbehind
Un lookaround controlla cosa viene prima o dopo una posizione senza includerlo nella corrispondenza. (?=…) richiede che qualcosa segua, (?!…) lo vieta; (?<=…) richiede che qualcosa preceda, (?<!…) lo vieta. Servono a individuare un numero dalla sua unità o valuta, o a sommare più condizioni sullo stesso testo.
\d+(?=%)Su «20% di 500»: trova 20, il numero seguito da un segno di percentuale, senza il segno.(?<=€)\d+Su «€25 e $30»: trova 25, l’importo dopo il simbolo dell’euro.^(?=.*\d).{8,}$Accetta password1 e rifiuta password: almeno otto caratteri, di cui uno è una cifra.
Flag
I flag cambiano il modo in cui si applica tutto il pattern. In JavaScript seguono la barra di chiusura, come in /sole/gi; nel tester hanno un campo apposito.
| Flag | Effetto |
|---|---|
g | trovare tutte le corrispondenze, non solo la prima |
i | ignorare la differenza tra maiuscole e minuscole |
m | ^ e $ valgono per ogni riga |
s | il punto trova anche gli a capo |
u | modalità Unicode: \p{…}, e un’emoji conta come un carattere |
v | modalità Unicode con operazioni sugli insiemi nelle classi; usarlo al posto di u |
y | adesivo (sticky): cercare solo dove è finita la corrispondenza precedente |
d | registrare dove inizia e finisce ogni gruppo |
Controllare email, date e altri formati
Una regex può controllare la forma di un dato, non se è vero. Lo standard HTML definisce cosa accetta un <input type="email"> con un pattern proprio, e dice apertamente che si scosta di proposito dalla RFC 5322, lo standard del formato email, che ammette forme come nomi tra virgolette con spazi che quasi nessuno usa. Accetta ana@example.com e anche ana@localhost; rifiuta ana@my_domain.example, perché un nome di dominio non può contenere il trattino basso. Se la casella esiste lo dice solo un messaggio.
Con le date è simile: ^\d{4}-\d{2}-\d{2}$ accetta 2026-02-31, che non esiste. Controlla la forma con il pattern, poi lascia decidere a una funzione di date se la data è valida. E non analizzare HTML o JSON con le espressioni regolari: le strutture annidate richiedono un parser.
Quando un pattern non finisce mai
La maggior parte dei motori, compresi quelli di JavaScript, Python e PHP, lavora con il backtracking: quando una strada fallisce, torna indietro e prova un altro modo di dividere il testo tra le parti del pattern. Un gruppo che ripete qualcosa che a sua volta si ripete, come ^(a+)+$, può essere diviso in un numero di modi che raddoppia a ogni carattere in più. Su una fila di a che finisce con un punto esclamativo il pattern non può mai corrispondere, ma il motore prova ogni divisione prima di arrendersi. OWASP lo chiama denial of service da espressioni regolari, ReDoS, e cita pattern come (a+)+$, ([a-zA-Z]+)*$ e (a|aa)+$.
Il rimedio è togliere l’ambiguità: ^a+$ descrive lo stesso testo e fallisce subito. Evita quantificatori annidati e alternative che possono trovare gli stessi caratteri, e non eseguire mai un pattern fornito da un utente senza un limite di tempo. Il tester di regex esegue ogni ricerca in un worker separato e la ferma dopo mezzo secondo, così un pattern del genere viene segnalato invece di bloccare la pagina.
Non tutti i motori sono uguali
Le basi qui sopra funzionano quasi ovunque, ma i dettagli cambiano da un linguaggio all’altro. Un pattern scritto per un motore va provato nell’altro prima di farci affidamento.
| Funzione | JavaScript | Python (re) | PHP (PCRE2) |
|---|---|---|---|
| Gruppo con nome | (?<nome>…) | (?P<nome>…) | entrambe le forme |
| \d e \w | solo ASCII | Unicode per impostazione predefinita | ASCII salvo con UCP |
| Quantificatori possessivi e gruppi atomici (a++, (?>…)) | no | sì, da Python 3.11 | sì |
| Flag in linea come (?i) | solo la forma limitata (?i:…), da ECMAScript 2025 | sì | sì |
Anche il lookbehind cambia: JavaScript ne accetta uno di lunghezza variabile, come (?<=[a-z]+), mentre Python richiede una larghezza fissa e lo rifiuta. Python scrive inoltre le barre rovesciate in modo diverso nel codice: una stringa raw, r"\d+", le lascia come sono, mentre i pattern JavaScript tra barre non hanno bisogno di raddoppiarle. In una stringa JavaScript passata a new RegExp(), ogni barra rovesciata va scritta due volte.
Domande frequenti
Perché il mio pattern trova una sola corrispondenza?
In JavaScript la ricerca si ferma alla prima corrispondenza se manca il flag g. Nel tester, aggiungi g ai flag.
Perché \w non trova le lettere accentate?
Perché in JavaScript \w indica solo dalla A alla Z, dalla a alla z, le cifre e il trattino basso. Usa \p{L} con il flag u per le lettere di qualunque alfabeto.
Come cerco un punto, una parentesi o un punto interrogativo?
Mettici davanti una barra rovesciata: \. \( \? e così via. Tra parentesi quadre quasi tutti perdono il loro significato, quindi [.?] trova un punto o un punto interrogativo.
Un’espressione regolare può validare un indirizzo email?
Solo la sua forma. Il pattern dello standard HTML è una buona scelta per i moduli; se l’indirizzo riceve posta lo si verifica solo inviando un messaggio.
Perché il mio pattern ha bloccato il programma?
Probabilmente ripete un gruppo che a sua volta si ripete, come (a+)+, e il testo corrisponde quasi. Riscrivilo senza quantificatori annidati ed esegui i pattern degli utenti con un limite di tempo.
Riferimenti
- MDN Web Docs — Regular expressions (JavaScript guide)
- MDN Web Docs — Regular expression syntax cheat sheet
- ECMAScript® Language Specification — RegExp (Regular Expression) Objects
- TC39 — Finished proposals (RegExp modifiers and duplicate named groups: ECMAScript 2025)
- Python documentation — re: regular expression operations
- PCRE2 — pcre2pattern: syntax and semantics of the regular expressions
- OWASP — Regular expression Denial of Service (ReDoS)
- WHATWG HTML Standard — Valid email address
- RFC 5322 — Internet Message Format (October 2008)
Guide correlate
- Indirizzi IP spiegati: IPv4, IPv6 e gli intervalli riservati
- Sottoreti e CIDR spiegati: prefissi, maschere e host utilizzabili
- Record DNS spiegati: A, AAAA, CNAME, MX, TXT e NS
- Un sito è down o il problema è solo tuo? Come verificarlo, passo per passo
- Codici di stato HTTP ed errori di Cloudflare: cosa significano 403, 404, 429, 500, 503 e 521
- Timestamp Unix e fusi orari spiegati
- Contare le date: giorni tra due date, anni bisestili e settimane ISO
- Fusi orari e ora legale: riunioni tra città
- Formati immagine spiegati: JPEG, PNG, WebP, AVIF e HEIC
- Le dimensioni di un’immagine spiegate: pixel, DPI, peso e qualità
- Le foto del telefono spiegate: HEIC, metadati EXIF e la tua privacy
- Robustezza delle password spiegata: lunghezza, entropia e passphrase
- Funzioni di hash spiegate: SHA-256, checksum e hashing delle password
- JSON Web Token spiegati: claim, firme e validazione sicura
- Le chiavi API spiegate: lunghezza, formato, archiviazione e rotazione
- Gli UUID spiegati: v4, v7 e come scegliere la versione giusta
- I codici QR spiegati: capacità, correzione degli errori e dimensioni di stampa
- Percentuali e IVA spiegate: aggiungere, scorporare e cumulare
- Errori comuni nel JSON e come correggerli
- I file CSV spiegati: separatori, virgolette, codifica ed Excel
- Le codifiche del testo spiegate: UTF-8, Base64 e codifica URL
- Modificare i PDF nel browser: cosa conservano unione, divisione e conversione