I pattern come un piccolo linguaggio
Un’espressione regolare (regex) descrive un insieme di stringhe. Invece di elencare ogni possibilità, scrivi un pattern: «tre cifre», «una forma simile a un’e-mail», «righe che iniziano con ERROR». Linguaggi di programmazione, editor e strumenti dati incorporano motori regex per cercare, validare ed estrarre testo.
I dialetti regex differiscono (JavaScript, Python, PCRE, Rust). Questa guida si concentra sui concetti condivisi dalla maggior parte dei motori moderni, con un’enfasi sulla sintassi in stile JavaScript usata nei browser. Prova i pattern in modo interattivo con lo strumento di test regex.
Letterali e metacaratteri
La maggior parte dei caratteri corrisponde a se stessa: cat corrisponde alle tre lettere c-a-t in ordine. I metacaratteri hanno un significato speciale: . ^ $ * + ? ( ) [ ] { } \ | (e altri a seconda del dialetto).
Per far corrispondere un metacarattere in modo letterale, esegui l’escape con un backslash: \. corrisponde a un punto. In alcuni linguaggi l’escape delle stringhe si sovrappone a quello regex—causando l’«inferno dei backslash». Preferisci le stringhe raw dove il linguaggio le offre.
Classi di caratteri
Le parentesi quadre elencano i caratteri consentiti: [abc] corrisponde ad a, b o c. Intervalli come [0-9] corrispondono alle cifre. La negazione [^0-9] corrisponde a un carattere che non è una cifra.
Le classi abbreviate (dipendenti dal flavor) includono \d (cifra), \w (carattere di parola), \s (spazio bianco). Le loro definizioni esatte variano—soprattutto per Unicode. Quando la validazione deve essere precisa, preferisci intervalli espliciti o escape di proprietà Unicode se il motore li supporta (\p{L} per le lettere in JS moderno).
Ancore
^ corrisponde all’inizio di una stringa (o di una riga in modalità multilinea). $ corrisponde alla fine. \b corrisponde a un confine di parola. Le ancore non consumano caratteri; affermano posizioni.
^\d{5}$ corrisponde a una stringa che è esattamente cinque cifre—non cinque cifre sepolte in un testo più lungo—quando le ancore vengono rispettate.
Quantificatori
I quantificatori specificano la ripetizione:
| Quantificatore | Significato |
|---|---|
* | zero o più |
+ | uno o più |
? | zero o uno |
{n} | esattamente n |
{n,} | n o più |
{n,m} | tra n e m inclusi |
Di default i quantificatori sono greedy: corrispondono quanto più possibile purché il pattern complessivo possa ancora riuscire. Le varianti lazy (*?, +?) corrispondono il meno possibile. Il matching greedy è una causa frequente di sorprese del tipo «il mio pattern ha mangiato l’intero documento».
Gruppi e alternanza
Le parentesi ( ... ) creano gruppi per quantificare sottopatterne e catturare sottostringhe. (ab)+ corrisponde ad ab, abab, ecc. L’alternanza | sceglie tra opzioni: cat|dog.
I gruppi non catturanti (?: ... ) raggruppano senza memorizzare una cattura—più puliti quando serve solo la struttura.
Le catture nominate esistono in molti motori ((?<year>\d{4})) e migliorano la leggibilità nelle estrazioni.
Trappole greedy e backtracking
Un pattern classico problematico per certi input assomiglia a (.*)+ o a quantificatori annidati ambigui. Il backtracking patologico può congelare un processo—ReDoS (regular expression denial of service). Per pattern forniti dall’utente in produzione, usa timeout, parser a sottoinsieme sicuro o motori con garanzie di tempo lineare. Per i tuoi pattern, preferisci classi di caratteri specifiche a .* quando possibile.
Flag / modificatori
Flag comuni:
i— senza distinzione maiuscole/minuscolem—^/$si applicano per rigas— il punto corrisponde alle newline (nei motori che lo supportano)g— trova tutte le corrispondenze (global)u— modalità Unicode in JavaScript
I flag cambiano il significato in modo sostanziale; documenta sempre quali flag usa un validatore.
Validazione versus estrazione
La validazione chiede se l’intero input corrisponde: spesso abbina un pattern ad ancore di inizio e fine, oppure usa metodi API con semantica di match completo. L’estrazione trova sottostringhe: usa API di ricerca/global e gruppi di cattura.
Non fingere che un regex e-mail lasco sia un validatore RFC completo. Per formati critici, combina regex con parser dedicati.
Una piccola scala di apprendimento
- Far corrispondere una parola fissa.
- Aggiungere una classe di caratteri per una cifra.
- Quantificare le cifre con
{n}. - Ancorare il pattern.
- Introdurre un gruppo opzionale.
- Estrarre una cattura.
- Solo dopo provare lookahead/lookbehind se il motore li supporta.
Saltare a un «regex e-mail perfetto» copiato da Internet insegna poco e spesso fallisce sui casi limite.
Abitudini di test
- Tenere una tabella di esempi che devono corrispondere e che non devono corrispondere.
- Includere stringa vuota, lunghezza massima e campioni Unicode.
- Testare input multilinea quando contano
mo il punto sulle newline. - In debug, semplificare il pattern finché la corrispondenza sorprendente scompare, poi ricostruire.
Lo strumento test-pattern ti aiuta a iterare sugli esempi prima di fissare un pattern nel codice applicativo.
Leggibilità e manutenzione
I regex lunghi sono write-once, read-never. Strategie:
- Spezzare la validazione in più controlli piccoli.
- Usare la modalità verbose/estesa se disponibile (Python
re.X) con commenti. - Assegnare il pattern a una costante con nome.
- Preferire parser per strutture annidate (HTML, JSON)—regex è lo strumento sbagliato per nesting arbitrario.
Lookahead e lookbehind (passo successivo opzionale)
Quando ancore e gruppi risultano familiari, molti motori offrono lookahead e lookbehind: asserzioni che verificano ciò che segue o precede una posizione senza consumare quei caratteri. Ad esempio, un lookahead positivo può richiedere che una stringa tipo password contenga una cifra da qualche parte senza spostare il cursore di match principale in modi scomodi. Queste funzionalità sono potenti e facili da abusare; tienile rare, commentale e verifica il comportamento nel dialetto esatto—il supporto ai lookbehind e le regole a larghezza fissa non sono identici ovunque.
Riepilogo
Le espressioni regolari descrivono forme di testo con letterali, classi, quantificatori, ancore e gruppi. Eccellono in validazioni ed estrazioni concise quando i pattern restano specifici e testati. Rispetta le differenze di dialetto, evita il backtracking catastrofico e passa a parser veri quando la complessità grammaticale supera una singola riga di punteggiatura.