Skip to content
Torna al blog
Tutorial

Cheat Sheet Regex: metacaratteri, gruppi e lookaround

Cheat sheet online di regex: metacaratteri, quantificatori, ancore, gruppi, lookaround e 15+ pattern JavaScript e Python. Stop al backtracking.

12 min di lettura

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 nginx e ti serve ogni richiesta 5xx da uno specifico user agent. Una regex di 40 caratteri con grep -E gira 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, ripgrep e 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

PatternTrova corrispondenza con
.Qualsiasi carattere tranne newline (o qualsiasi carattere con il flag s/dotall)
\dUna cifra ([0-9], o tutte le cifre Unicode con il flag u)
\DUna non-cifra
\wUn carattere di parola ([A-Za-z0-9_])
\WUn carattere non di parola
\sQualsiasi spazio bianco (spazio, tab, newline, …)
\SQualsiasi carattere non whitespace

Quantificatori

PatternTrova 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

PatternTrova corrispondenza con
^Inizio della stringa (o inizio di riga con il flag m)
$Fine della stringa (o fine di riga con il flag m)
\bConfine di parola
\BNon-confine di parola
\AInizio assoluto della stringa (Python)
\ZFine assoluta della stringa (Python)

Classi di caratteri

PatternTrova 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

PatternTrova corrispondenza con
(...)Gruppo di cattura
(?:...)Gruppo non di cattura
(?<name>...)Cattura con nome (JS ES2018+); Python usa (?P<name>...)
\1, \2Backreference al gruppo 1, 2

Lookaround

PatternTrova corrispondenza con
(?=...)Lookahead positivo
(?!...)Lookahead negativo
(?<=...)Lookbehind positivo
(?<!...)Lookbehind negativo

Flag

FlagEffetto
iCase-insensitive
mMultiline: ^ e $ corrispondono riga per riga
sDotall: . corrisponde anche ai newline
gGlobal (JS): trova tutte le corrispondenze
uModalità Unicode
ySticky (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 POSIXTrova corrispondenza conEquivalente 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:

  1. Appiattisci il pattern: /a+b/ fa lo stesso lavoro senza annidamento.
  2. Usa un gruppo atomico (Python regex, PCRE, Java, Ruby): (?>a+)+b. Una volta che a+ ha trovato corrispondenza, il motore rifiuta di tornare indietro al suo interno.
  3. Cambia motore: il regexp di Go, RE2 e il crate regex di 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.

MotoreLunghezza del lookbehind
JavaScript (V8, SpiderMonkey, JSC 16.4+)Larghezza variabile da ES2018. (?<=\d+) è valido.
Stdlib re di PythonSolo larghezza fissa. (?<=\d+) lancia error: look-behind requires fixed-width pattern.
Pacchetto PyPI regex di PythonLarghezza 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:

  1. Analisi statica: strumenti come safe-regex, recheck e la regola ESLint no-misleading-character-class intercettano i pattern pericolosi prima che vengano spediti.
  2. Gruppi atomici (Python regex, PCRE, Ruby, Java): (?>...) impedisce al motore di rientrare nel gruppo durante il backtrack.
  3. Quantificatori possessive (*+, ++, ?+ in PCRE/Java): stessa idea, sintassi più stringata.
  4. Passa a un motore non-backtracking: il regexp di Go, RE2, il crate regex di Rust e il binding Python re2 girano tutti in tempo lineare. ripgrep è il deployment RE2 più diffuso in circolazione.
  5. 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.

Articoli correlati

Vedi tutti gli articoli