Cheat Sheet Regex: metacaratteri, gruppi e lookaround (riferimento completo)
Un’espressione regolare è un piccolo linguaggio di pattern per trovare corrispondenze nel testo: \d+ significa “una o più cifre”, ^Error significa “una riga che inizia con Error”. Il lavoro è tutto qui. Questa cheat sheet regex ti dà la sintassi in una sola pagina scorrevole: metacaratteri, quantificatori, ancore, gruppi, lookaround, flag, più 15+ pattern che puoi incollare oggi in JavaScript o Python.
Serve a sviluppatori che già sanno cos’è una stringa e vogliono un riferimento, non una visita guidata. Se ti servono solo i simboli, salta alla Tabella di riferimento rapido. Se ti è mai capitato che una regex bloccasse un server, vai dritto alle sezioni su lookaround e insidie.
1. Cos’è una regex e perché ti serve ancora nel 2026
Una regex è un pattern compilato in una macchina a stati che scansiona una stringa e o trova corrispondenza o fallisce. La grammatica è piccola, i casi d’uso no.
L’AI può buttare giù un pattern per te, ma tre compiti restano in mano a chi scrive regex a mano:
- Analisi dei log: hai dieci milioni di righe di access log
nginxe ti serve ogni richiesta 5xx da uno specifico user agent. Una regex di 40 caratteri congrep -Egira in pochi secondi; una chiamata LLM per riga no. - Validazione di form e campi: numeri di telefono, codici postali, timestamp ISO, chiavi di licenza. Il pattern vive accanto all’input e gira a ogni pressione di tasto nel browser.
- Find-and-replace di massa: refactoring di mille file in cui devi catturare un nome e reiniettarlo.
sed,ripgrepe la funzione “Sostituisci nei file” del tuo editor parlano regex in modo nativo.
Per la metà JSON dello stesso toolkit, vedi la nostra jq command-line cheat sheet.
1.1 Come si legge un pattern regex (tutorial lampo da 5 secondi)
La maggior parte dei pattern si legge meglio da sinistra a destra, un token alla volta. Prendi ^[A-Z]\w+\d{2,4}$ come esempio:
^ancora la corrispondenza all’inizio della stringa.[A-Z]corrisponde esattamente a una lettera maiuscola.\w+corrisponde a uno o più caratteri di parola.\d{2,4}corrisponde a un numero di cifre compreso tra due e quattro.$ancora alla fine della stringa.
La pratica sta nel leggere prima le ancore, poi le classi di caratteri, infine i quantificatori.
2. Tabella di riferimento rapido
È la sezione per cui la maggior parte dei lettori è arrivata qui. Copia ciò che ti serve.
Metacaratteri
| Pattern | Trova corrispondenza con |
|---|---|
. | Qualsiasi carattere tranne newline (o qualsiasi carattere con il flag s/dotall) |
\d | Una cifra ([0-9], o tutte le cifre Unicode con il flag u) |
\D | Una non-cifra |
\w | Un carattere di parola ([A-Za-z0-9_]) |
\W | Un carattere non di parola |
\s | Qualsiasi spazio bianco (spazio, tab, newline, …) |
\S | Qualsiasi carattere non whitespace |
Quantificatori
| Pattern | Trova corrispondenza con |
|---|---|
* | 0 o più (greedy) |
+ | 1 o più (greedy) |
? | 0 o 1 (greedy) |
{n} | Esattamente n volte |
{n,m} | Tra n e m volte |
{n,} | n o più volte |
*?, +?, ??, {n,m}? | Versioni lazy di ciascun quantificatore |
Ancore
| Pattern | Trova corrispondenza con |
|---|---|
^ | Inizio della stringa (o inizio di riga con il flag m) |
$ | Fine della stringa (o fine di riga con il flag m) |
\b | Confine di parola |
\B | Non-confine di parola |
\A | Inizio assoluto della stringa (Python) |
\Z | Fine assoluta della stringa (Python) |
Classi di caratteri
| Pattern | Trova corrispondenza con |
|---|---|
[abc] | Uno tra a, b, c |
[^abc] | Qualsiasi cosa tranne a, b, c |
[a-z] | Qualsiasi lettera minuscola |
[0-9] | Qualsiasi cifra |
\p{L} | Qualsiasi lettera Unicode (flag u in JS, default nel modulo re di Python) |
Gruppi
| Pattern | Trova corrispondenza con |
|---|---|
(...) | Gruppo di cattura |
(?:...) | Gruppo non di cattura |
(?<name>...) | Cattura con nome (JS ES2018+); Python usa (?P<name>...) |
\1, \2 | Backreference al gruppo 1, 2 |
Lookaround
| Pattern | Trova corrispondenza con |
|---|---|
(?=...) | Lookahead positivo |
(?!...) | Lookahead negativo |
(?<=...) | Lookbehind positivo |
(?<!...) | Lookbehind negativo |
Flag
| Flag | Effetto |
|---|---|
i | Case-insensitive |
m | Multiline: ^ e $ corrispondono riga per riga |
s | Dotall: . corrisponde anche ai newline |
g | Global (JS): trova tutte le corrispondenze |
u | Modalità Unicode |
y | Sticky (JS): ancorato a lastIndex |
3. Metacaratteri e classi di caratteri
3.1 Letterali vs caratteri speciali
La maggior parte dei caratteri è letterale. I 12 metacaratteri che devi escapare quando li vuoi come se stessi sono:
. ^ $ * + ? ( ) [ ] { } | \
Dimenticare l’escape di . è il bug regex più comune. \. corrisponde a un punto letterale. Dentro una classe di caratteri, anche [.] corrisponde a un punto letterale: la maggior parte dei metacaratteri perde il proprio potere dentro [...] tranne ], \, ^ (quando è il primo) e - (in mezzo).
3.2 Shorthand di caratteri
Le classi shorthand sembrano semplici finché non spunta Unicode:
// JavaScript — senza il flag u, \d è solo ASCII
/\d/.test('5'); // true
/\d/.test('٥'); // false (cifra araba-indica)
/\d/u.test('٥'); // false — anche con u, \d resta ASCII in JS
/\p{N}/u.test('٥'); // true — \p{N} è la classe di cifre Unicode-aware
# Python — il modulo re tratta \d come Unicode per default
import re
re.match(r'\d', '٥') # <Match span=(0, 1)>
re.match(r'(?a)\d', '٥') # None — (?a) forza ASCII
Se gestisci solo input in ASCII inglese, \d e [0-9] sono intercambiabili. Nel momento in cui un utente incolla un nome con un accento, vuoi \p{L} al posto di \w.
3.3 Classi di caratteri personalizzate
// JavaScript
/[A-Za-z][A-Za-z0-9_-]{2,29}/.test('valid_handle-1'); // true
// Negazione e range combinati
/[^aeiou\s]/g // qualsiasi carattere non vocale e non whitespace
Per le categorie Unicode, \p{L} è “qualsiasi lettera”, \p{N} è “qualsiasi numero”, \p{Script=Han} è “qualsiasi carattere Han”. JavaScript richiede il flag u. Python supporta \p{...} solo tramite il pacchetto PyPI regex, non con il modulo re della stdlib.
Se lavori da riga di comando, potresti incontrare anche le classi di caratteri POSIX:
| Classe POSIX | Trova corrispondenza con | Equivalente ASCII |
|---|---|---|
[[:alpha:]] | lettere | [A-Za-z] |
[[:digit:]] | cifre | [0-9] (\d) |
[[:alnum:]] | lettere + cifre | [A-Za-z0-9] |
[[:space:]] | whitespace | \s |
[[:upper:]] | maiuscole | [A-Z] |
[[:lower:]] | minuscole | [a-z] |
Le classi POSIX funzionano in grep -E, sed -E. Non funzionano in JavaScript né nel modulo re di Python: lì usa \d, \s, \w.
4. Quantificatori e greedy vs lazy
4.1 Quantificatori di base
/a*/.exec('aaab') // ['aaa'] — 0 o più
/a+/.exec('aaab') // ['aaa'] — 1 o più
/a?/.exec('aaab') // ['a'] — 0 o 1
/a{2,3}/.exec('aaaab') // ['aaa'] — da 2 a 3
4.2 Greedy vs lazy
Per default i quantificatori sono greedy: prendono il più possibile, poi cedono terreno per far quadrare l’intero pattern. Aggiungi ? per renderli lazy.
const html = '<p>one</p><p>two</p>';
html.match(/<p>.*<\/p>/)[0]; // '<p>one</p><p>two</p>' (greedy si mangia entrambi)
html.match(/<p>.*?<\/p>/)[0]; // '<p>one</p>' (lazy si ferma al primo)
La versione lazy è quasi sempre ciò che vuoi per estrarre tag o stringhe tra virgolette. Meglio ancora: evita del tutto . e usa una classe negata. <p>[^<]*</p> è più veloce di <p>.*?</p> perché non c’è nulla in cui fare backtracking.
4.3 Backtracking catastrofico
È così che una regex blocca un server. Annidi un quantificatore dentro un altro quantificatore con una sovrapposizione ambigua, e il motore esplora un numero esponenziale di percorsi prima di arrendersi.
// Non farlo
/(a+)+b/.test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!'); // ci mette secondi
Per 41 a seguite da !, il motore prova all’incirca 2^41 punti di split prima di decidere che la b manca. Tre soluzioni:
- Appiattisci il pattern:
/a+b/fa lo stesso lavoro senza annidamento. - Usa un gruppo atomico (Python
regex, PCRE, Java, Ruby):(?>a+)+b. Una volta chea+ha trovato corrispondenza, il motore rifiuta di tornare indietro al suo interno. - Cambia motore: il
regexpdi Go, RE2 e il crateregexdi Rust usano un NFA a tempo lineare e per design non possono fare backtracking catastrofico.
JavaScript e il re di Python fanno entrambi backtracking e non hanno gruppi atomici nella stdlib (il pacchetto PyPI regex di Python li aggiunge). Quando controlli la lunghezza dell’input va bene; quando l’input viene da un utente, valida prima la lunghezza o precompila con RE2.
5. Ancore e confini di parola
5.1 ^ e $
Per default, ^ è l’inizio dell’intero input e $ è la fine. Con il flag m (multiline) diventano l’inizio e la fine di ogni riga:
const log = 'INFO start\nERROR boom\nINFO done';
log.match(/^ERROR.*/); // null — modalità single-line, ^ corrisponde solo all'indice 0
log.match(/^ERROR.*/m); // ['ERROR boom']
5.2 \b e \B
\b è un’asserzione a larghezza zero: trova corrispondenza nella posizione tra un carattere di parola (\w) e un carattere non di parola. Utile per la ricerca di parole intere:
/\bcat\b/.test('the cat sat'); // true
/\bcat\b/.test('concatenate'); // false
I confini di parola sono definiti su \w, che per default è ASCII. Cinese, giapponese e coreano non hanno spazi tra le parole, quindi \b non rileva i confini di parola lì: ti serve un tokenizer (jieba, MeCab) prima della regex, non al posto suo.
5.3 Modalità multiline
import re
text = "INFO ok\nERROR fail\nINFO done\n"
re.findall(r'^ERROR.*$', text) # []
re.findall(r'^ERROR.*$', text, re.MULTILINE) # ['ERROR fail']
In JavaScript la stessa cosa si scrive text.match(/^ERROR.*$/gm). Combina m con g per prendere ogni riga corrispondente.
6. Gruppi, cattura e backreference
6.1 Gruppi di cattura
Le parentesi fanno due lavori: raggruppano sotto-pattern per i quantificatori e catturano la corrispondenza per usarla dopo.
'2026-05-13'.match(/(\d{4})-(\d{2})-(\d{2})/);
// ['2026-05-13', '2026', '05', '13', index: 0, ...]
I gruppi sono numerati da sinistra a destra in base alla loro parentesi di apertura, partendo da 1.
6.2 Gruppi non di cattura
Quando ti serve solo il raggruppamento, non la cattura, usa (?:...). È più veloce e tiene in ordine i gruppi numerati:
/(?:https?):\/\/(\S+)/.exec('see https://go-tools.org');
// ['https://go-tools.org', 'go-tools.org']
// — il protocollo è raggruppato ma non catturato; il gruppo 1 è l'host
6.3 Gruppi con nome
Dare un nome ai gruppi rende i pattern leggibili e a prova di refactoring.
// JavaScript (ES2018+)
const m = '2026-05-13'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
m.groups.year; // '2026'
# Python — nota la sintassi (?P<...>)
import re
m = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', '2026-05-13')
m.group('year') # '2026'
6.4 Backreference
Le backreference permettono a una parte successiva del pattern di ripetere ciò che una cattura precedente ha trovato.
// Trova qualsiasi carattere che si ripete consecutivamente
'bookkeeper'.match(/(\w)\1/g); // ['oo', 'kk', 'ee']
// Trova tag HTML accoppiati per nome
const tag = /<(\w+)>(.*?)<\/\1>/;
'<b>bold</b>'.match(tag);
// ['<b>bold</b>', 'b', 'bold']
In Python, \1 funziona sia nel pattern sia nella sostituzione. I riferimenti con nome si scrivono (?P=name) nel pattern e \g<name> nelle sostituzioni di re.sub.
7. Lookaround: lookahead e lookbehind
I lookaround sono asserzioni a larghezza zero. Verificano una condizione senza consumare caratteri, quindi puoi concatenarli.
7.1 Lookahead
// Password: almeno 8 caratteri, una cifra, una maiuscola, una minuscola
const strong = /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z]).{8,}$/;
strong.test('Hunter2!'); // true
strong.test('hunter2!'); // false — nessuna maiuscola
// Lookahead negativo — nomi di file che non sono .tmp
/^[\w-]+(?!\.tmp$)\.[a-z]+$/.test('report.csv'); // true
7.2 Lookbehind
Il lookbehind è l’immagine speculare: asserisce ciò che viene prima della posizione corrente.
// Estrai un prezzo dopo un simbolo di valuta — tieni il numero, scarta il $
'price: $42.50'.match(/(?<=\$)\d+(\.\d+)?/); // ['42.50', '.50']
// Lookbehind negativo — trova Bond ma non James Bond
'Mr. Bond'.match(/(?<!James )Bond/); // ['Bond']
'James Bond'.match(/(?<!James )Bond/); // null
7.3 Lookbehind in JavaScript vs Python
È uno dei pochi punti in cui i due motori divergono abbastanza da rompere un pattern quando lo porti da una parte all’altra.
| Motore | Lunghezza del lookbehind |
|---|---|
| JavaScript (V8, SpiderMonkey, JSC 16.4+) | Larghezza variabile da ES2018. (?<=\d+) è valido. |
Stdlib re di Python | Solo larghezza fissa. (?<=\d+) lancia error: look-behind requires fixed-width pattern. |
Pacchetto PyPI regex di Python | Larghezza variabile supportata. import regex; regex.search(r'(?<=\d+)abc', '12abc'). |
Workaround in Python: riscrivi il lookbehind con una ripetizione nota ((?<=\d{3})) oppure cattura il prefisso e ritaglialo dopo la corrispondenza.
8. Flag e modificatori
8.1 i — case-insensitive
/error/i.test('FATAL ERROR'); // true
re.search(r'error', 'FATAL ERROR', re.IGNORECASE) # <Match span=(6, 11)>
8.2 m e s
m trasforma ^ e $ in ancore per ogni riga. s (dotall) fa sì che . corrisponda anche ai newline. Sono indipendenti: combinali quando li vuoi entrambi.
/<script>(.*?)<\/script>/s.exec('<script>\nalert(1)\n</script>')[1];
// '\nalert(1)\n' — senza s, il . rifiuterebbe i newline
8.3 g — global
In JavaScript, g cambia l’API più che la corrispondenza in sé. Senza g, String.match restituisce i gruppi di cattura; con g, restituisce ogni stringa corrispondente. Usa matchAll per mantenere i gruppi di cattura su tutte le corrispondenze.
const text = 'a=1 b=2 c=3';
text.match(/(\w)=(\d)/); // prima corrispondenza con i gruppi
text.match(/(\w)=(\d)/g); // ['a=1', 'b=2', 'c=3'] — niente gruppi
[...text.matchAll(/(\w)=(\d)/g)]; // ogni corrispondenza, con i gruppi
Python non usa g. re.findall, re.finditer e re.sub sono le varianti globali.
8.4 u — Unicode e \p{...}
// Trova qualsiasi carattere Han (cinese, kanji giapponesi)
/\p{Script=Han}+/gu.test('Hello 世界'); // true
// Trova emoji (extended pictographic)
/\p{Extended_Pictographic}/u.test('👋'); // true
In Python, Unicode è attivo per default. re.findall(r'[一-鿿]+', text) è l’equivalente per il range Han. Per le property escape Unicode complete, usa il pacchetto PyPI regex: regex.findall(r'\p{Script=Han}+', text).
9. Pattern comuni che userai ogni giorno
9.1 Validazione email
Sii onesto su quale versione ti serve.
// Il pattern al 95% — quello che usa la maggior parte dei validatori di form
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
email.test('a@b.co'); // true
// Il pattern "voglio davvero qualcosa di RFC 5322-ish"
const rfc = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
La verità: la validazione email full RFC 5322 in pura regex è di circa 6000 caratteri e sbaglia comunque sui casi limite. Usa il pattern al 95%, poi manda una email di verifica: è l’unico test che funziona davvero.
9.2 Estrazione URL
const urlPattern = /https?:\/\/[^\s<>"]+/g;
const found = 'See https://example.com/a?b=1 and http://x.io'.match(urlPattern);
// ['https://example.com/a?b=1', 'http://x.io']
Una volta estratto un URL, di solito vuoi ispezionarne la query string. Incollalo nel nostro Codificatore e Decodificatore URL e potrai leggere i parametri percent-encoded a colpo d’occhio. Per il quadro completo di quando codificare e quando decodificare, leggi la Codifica e decodifica URL: guida online alla codifica percentuale.
9.3 Numeri di telefono
// E.164 — internazionale, + opzionale e prefisso paese da 1-3 cifre
const e164 = /^\+?[1-9]\d{1,14}$/;
e164.test('+14155551234'); // true
// North American Number Plan con separatori
const nanp = /^(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/;
nanp.test('(415) 555-1234'); // true
Per qualsiasi cosa oltre “questa forma è plausibile?”, usa libphonenumber. La regex non può validare che un prefisso esista davvero.
9.4 IPv4 e IPv6
// IPv4 — strettamente 0-255 per ottetto
const ipv4 = /^((25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(25[0-5]|2[0-4]\d|1?\d?\d)$/;
ipv4.test('192.168.1.1'); // true
ipv4.test('999.0.0.1'); // false
// IPv6 — la forma semplificata. Il pattern RFC 4291 completo è ~600 caratteri.
const ipv6simple = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
ipv6simple.test('2001:0db8:85a3:0000:0000:8a2e:0370:7334'); // true
Per un IPv6 reale con shorthand ::, IPv4 incorporato e zone identifier, usa isIP() di node:net o ipaddress.ip_address() di Python. Provare a farlo in pura regex è un rito di passaggio, poi diventa un peso di manutenzione.
9.5 Date e timestamp ISO 8601
// Solo data — YYYY-MM-DD
const isoDate = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
isoDate.test('2026-05-13'); // true
// Data + ora + frazioni di secondo opzionali + Z o offset
const iso = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
iso.test('2026-05-13T09:30:00.123Z'); // true
ISO 8601 sembra semplice ed è pieno di trappole: leap second, date settimanali (2026-W19), date ordinali (2026-133). Per epoch in secondi rispetto a millisecondi e cambi di fuso, vedi la Guida online ai timestamp Unix: precisione, fuso e ora legale.
10. Workflow di find/replace con la regex
10.1 JavaScript — String.replace con $1
// Riformatta date USA: MM/DD/YYYY -> YYYY-MM-DD
'05/13/2026'.replace(/(\d{2})\/(\d{2})\/(\d{4})/, '$3-$1-$2');
// '2026-05-13'
// Usa una callback quando la sostituzione è condizionale
'price 42 dollars'.replace(/(\d+) dollars/, (_, n) => `$${n}`);
// 'price $42'
$1, $2, … fanno riferimento ai gruppi numerati. $<name> ai gruppi con nome. $& è l’intera corrispondenza; $$ è un $ letterale.
10.2 Python — re.sub con \1 e callback
import re
# Stesso riformato data di sopra
re.sub(r'(\d{2})/(\d{2})/(\d{4})', r'\3-\1-\2', '05/13/2026')
# '2026-05-13'
# Callback — porta in maiuscolo ogni indirizzo email in una stringa
def upper_email(m):
return m.group(0).upper()
re.sub(r'[\w.-]+@[\w.-]+', upper_email, 'mail me at hi@go-tools.org')
# 'mail me at HI@GO-TOOLS.ORG'
Nelle sostituzioni Python usa \1 o \g<name>. Il prefisso raw string r'...' conta: senza, \1 diventa un carattere letterale.
10.3 CLI: sed, grep, ripgrep, jq
Per refactoring batch da riga di comando, la regex si sposta dallo script alla shell:
# ripgrep — trova ogni TODO con un nome attaccato
rg -n '\bTODO\(([^)]+)\)' --replace 'TODO(\1)'
# grep -E con ancore — righe di login falliti da auth.log
grep -E '^[A-Z][a-z]{2} +[0-9]+ .*Failed password' /var/log/auth.log
# sed — rimuovi trailing whitespace, in-place, su tutto un albero
find . -name '*.md' -print0 | xargs -0 sed -i -E 's/[[:space:]]+$//'
ripgrep usa il crate regex di Rust (stile RE2, tempo lineare, niente lookbehind). grep -E e sed -E usano regex estesa POSIX, che non ha \d: usa [0-9] e [[:digit:]]. Quando i dati sono JSON, sostituisci la regex con jq: vedi la jq cheat sheet per una scheda di riferimento parallela.
11. Insidie comuni
11.1 Dimenticare l’escape di .
Un bug vero che abbiamo spedito in produzione: un redactor di log doveva mascherare indirizzi IP.
// Sbagliato — corrisponde anche a '192a168b1c1'
/(\d+).(\d+).(\d+).(\d+)/.test('192a168b1c1'); // true
// Giusto
/(\d+)\.(\d+)\.(\d+)\.(\d+)/.test('192a168b1c1'); // false
Dentro una classe di caratteri, . è già letterale, quindi [.] e \. funzionano entrambi. Altrove, fai l’escape.
11.2 .* greedy mangia troppo
'<a href="x"><b>bold</b></a>'.match(/<(.*)>/)[1];
// 'a href="x"><b>bold</b></a' — tutto quanto!
Il .* greedy scansiona fino alla fine della stringa e poi torna indietro finché > non trova corrispondenza: l’ultimo > dell’input. O passi in modalità lazy (.*?) oppure, più veloce e più chiaro, usi una classe negata ([^>]*).
11.3 Ancore multiline
Una confusione comune: ^ e $ per default non corrispondono ai caratteri newline. Corrispondono alle posizioni all’inizio e alla fine dell’intero input. Aggiungere il flag m è ciò che li trasforma in ancore per ogni riga. Aggiungere il flag s è ciò che fa attraversare a . i newline. Sono ortogonali, e per l’analisi dei log di solito li vuoi entrambi.
11.4 ReDoS e come disinnescarlo
ReDoS, regex denial of service, è la versione production-grade del backtracking catastrofico. Le soluzioni:
- Analisi statica: strumenti come
safe-regex,rechecke la regola ESLintno-misleading-character-classintercettano i pattern pericolosi prima che vengano spediti. - Gruppi atomici (Python
regex, PCRE, Ruby, Java):(?>...)impedisce al motore di rientrare nel gruppo durante il backtrack. - Quantificatori possessive (
*+,++,?+in PCRE/Java): stessa idea, sintassi più stringata. - Passa a un motore non-backtracking: il
regexpdi Go, RE2, il crateregexdi Rust e il binding Pythonre2girano tutti in tempo lineare. ripgrep è il deployment RE2 più diffuso in circolazione. - Valida prima la lunghezza dell’input. Una regex bomb da 10 KB è un bug; un cap di 10 byte sull’input è una riga di codice.
Per un inventario più ampio degli strumenti di uso quotidiano che si abbinano alla regex (formatter, decoder, convertitori) vedi la nostra Strumenti online essenziali: codifica e hashing (2026).
Prima di spedire un pattern complesso, mettilo alla prova in modo interattivo. regex101.com passa tra le varianti PCRE, JavaScript, Python e Go, spiega ogni token e mostra il backtracking, così puoi individuare i pattern catastrofici.
12. FAQ
Qual è la differenza tra * e + nelle regex?
* trova zero o più occorrenze (può corrispondere a una stringa vuota); + trova una o più (ne serve almeno una). a* corrisponde a '', 'a', 'aaaa'. a+ corrisponde a 'a' e 'aaaa' ma non a ''.
Come trovo corrispondenze su più righe con la regex?
Attiva il flag multiline, /.../m in JavaScript, re.MULTILINE in Python, così ^ e $ si ancorano a ogni riga. Per far attraversare anche a . i newline, aggiungi il flag dotall (s in JavaScript, re.DOTALL in Python).
La regex è uguale in JavaScript e Python?
La sintassi di base (quantificatori, ancore, classi di caratteri, gruppi base) è uguale al 90%. Le due differenze reali: JavaScript (ES2018+) supporta il lookbehind a larghezza variabile e scrive i gruppi con nome come (?<name>...); il re della stdlib di Python richiede lookbehind a larghezza fissa e usa (?P<name>...). Per lookbehind a larghezza variabile in Python, installa il pacchetto regex da PyPI.
Perché la mia regex ha backtracking catastrofico?
Hai quantificatori annidati con corrispondenze che si sovrappongono, tipo (a+)+ o (a|a)*. Su un input che quasi combacia ma fallisce verso la fine, il motore prova ogni split del quantificatore interno: un numero esponenziale di percorsi. Sistemalo con un gruppo atomico (?>a+)+, un quantificatore possessive a++, o passando a un motore non-backtracking come RE2 o il regexp di Go.
Posso usare il lookbehind in JavaScript?
Sì. Il lookbehind positivo (?<=...) e negativo (?<!...) sono in V8 (Chrome, Node.js), SpiderMonkey (Firefox) e JavaScriptCore (Safari 16.4+) da ES2018. Il lookbehind a larghezza variabile è supportato. Per Safari più vecchi, transpila con Babel o fai feature detection con un try/catch intorno a new RegExp.
Come trovo corrispondenza con un punto . letterale nella regex?
Fai l’escape con un backslash: \. corrisponde a un punto letterale. Dentro una classe di caratteri il punto è già letterale: sia [.] sia [\.] funzionano. Fuori da una classe, un . non escapato è un metacarattere che significa “qualsiasi carattere tranne newline” (o qualsiasi carattere in assoluto con il flag dotall).
Cosa significa \s nelle regex?
\s corrisponde a qualsiasi carattere di spazio bianco: spazio, tab, newline, ritorno a capo. In modalità Unicode trova anche NBSP. \S è l’inverso.
Le espressioni regolari fanno distinzione tra maiuscole e minuscole?
Sì, per default. Usa il flag i in JavaScript (/cat/i) oppure re.IGNORECASE / (?i) in Python.