W3docs

Python RegEx

Impara le espressioni regolari Python: sintassi, sequenze speciali, gruppi, lookahead, flag e funzioni del modulo re con esempi chiari.

Le espressioni regolari (regex) ti permettono di cercare, estrarre e sostituire testo in base a pattern flessibili anziché stringhe esatte. Il modulo integrato re di Python fornisce tutto il necessario. Questo capitolo copre l'intero toolkit regex: sintassi, sequenze speciali, quantificatori, gruppi, lookahead/lookbehind, flag e ogni funzione chiave di re — con esempi corretti ed eseguibili.

Perché usare le espressioni regolari?

I metodi string semplici (str.find(), str.replace(), str.split()) funzionano bene per testo fisso. Le espressioni regolari eccellono quando il pattern varia:

  • Verificare che una string assomigli a un indirizzo email o numero di telefono.
  • Estrarre tutte le date da un documento, indipendentemente dal formato esatto.
  • Rimuovere i tag HTML da una string.
  • Sostituire più sequenze di spazi bianchi diverse con un singolo spazio.

Quando il compito riguarda descrivere una forma piuttosto che un valore fisso, ricorri a re.

Raw String

I pattern regex sono quasi sempre scritti come raw string (r'...'). Le raw string trattano i backslash come caratteri letterali, il che è importante perché la regex usa molte sequenze con backslash (\d, \w, \s, \b). Senza il prefisso r occorrerebbero doppi backslash ovunque:

import re

# Both patterns are identical — raw string is easier to read
re.findall(r'\d+', 'abc 123')   # raw:    r'\d+'
re.findall('\\d+', 'abc 123')   # normal: '\\d+'

Usa le raw string per ogni pattern regex — è la convenzione universale.

Sintassi di base: i metacaratteri

I metacaratteri sono caratteri con significato speciale all'interno di un pattern. I caratteri letterali corrispondono esattamente a se stessi.

MetacarattereSignificato
.Qualsiasi carattere tranne un'a capo
^Inizio della string (o della riga in modalità MULTILINE)
$Fine della string (o della riga in modalità MULTILINE)
*Zero o più occorrenze dell'elemento precedente
+Una o più occorrenze dell'elemento precedente
?Zero o una occorrenza dell'elemento precedente
{n}Esattamente n ripetizioni
{n,m}Tra n e m ripetizioni
[...]Classe di caratteri — uno qualsiasi dei caratteri elencati
[^...]Classe negata — qualsiasi carattere non elencato
|Alternanza — l'espressione sinistra o quella destra
()Gruppo di cattura
\Escape di un metacarattere, o inizio di una sequenza speciale

Per corrispondere a un metacarattere letterale come . o *, aggiunge un backslash davanti: \. corrisponde a un punto reale.

Sequenze speciali

Le sequenze speciali sono classi di caratteri abbreviate che compaiono frequentemente nei pattern reali.

SequenzaCorrisponde a
\dQualsiasi cifra — equivale a [0-9]
\DQualsiasi non-cifra
\wCarattere alfanumerico: lettere, cifre, underscore
\WCarattere non alfanumerico
\sSpazio bianco: spazio, tab, a capo
\SNon-spazio bianco
\bConfine di parola (zero-width)
\BNon-confine
import re

print(re.findall(r'\d+', 'I have 3 cats and 12 dogs'))
# ['3', '12']

print(re.findall(r'\w+', 'hello_world 123'))
# ['hello_world', '123']

print(re.findall(r'\bPython\b', 'Python Pythonista Python3'))
# ['Python']  — word boundary prevents partial matches

\b è particolarmente utile: corrisponde alla posizione tra un carattere alfanumerico e uno non alfanumerico, quindi \bPython\b corrisponde alla parola standalone "Python" ma non a "Pythonista" o "Python3".

Quantificatori

I quantificatori controllano quante volte deve corrispondere l'elemento precedente.

import re

print(re.findall(r'a*', 'baaa'))   # ['', 'aaa', '']
print(re.findall(r'a+', 'baaa'))   # ['aaa']
print(re.findall(r'a?', 'baaa'))   # ['', 'a', 'a', 'a', '']
print(re.findall(r'a{3}', 'baaa')) # ['aaa']

Corrispondenza greedy vs lazy

Per impostazione predefinita, i quantificatori sono greedy — corrispondono al massimo testo possibile. Aggiungi ? dopo il quantificatore per renderlo lazy (corrisponde al minimo possibile).

import re

html = '<b>bold</b> and <i>italic</i>'

print(re.findall(r'<.*>', html))
# ['<b>bold</b> and <i>italic</i>']  — greedy: matches from first < to last >

print(re.findall(r'<.*?>', html))
# ['<b>', '</b>', '<i>', '</i>']  — lazy: matches each individual tag

I quantificatori lazy sono essenziali quando si analizza testo strutturato come HTML o frammenti JSON.

Classi di caratteri

Una classe di caratteri [...] corrisponde a uno qualsiasi dei caratteri dell'insieme specificato. Usa - per gli intervalli e ^ all'inizio per negare la classe.

import re

print(re.findall(r'[aeiou]', 'hello world'))
# ['e', 'o', 'o']

print(re.findall(r'[0-9]', 'a1b2c3'))
# ['1', '2', '3']

print(re.findall(r'[^aeiou\s]+', 'hello world'))
# ['h', 'll', 'w', 'rld']  — consonants only (not vowels, not spaces)

Intervalli pronti all'uso comuni: [a-z] lettere minuscole, [A-Z] maiuscole, [0-9] cifre, [a-zA-Z0-9] alfanumerici.

Ancore

Le ancore non consumano caratteri — asseriscono una posizione nella string.

import re

print(re.findall(r'^Python', 'Python is great'))
# ['Python']  — matches only if 'Python' is at the start

print(re.findall(r'great$', 'Python is great'))
# ['great']  — matches only if 'great' is at the end

print(re.findall(r'^Python', 'Learn Python'))
# []  — 'Python' is not at the start of this string

Consulta il flag re.MULTILINE più avanti in questo capitolo per applicare ^ e $ per riga anziché per string.

Alternanza

Il pipe | funziona come un OR logico tra due espressioni.

import re

print(re.findall(r'cat|dog', 'I have a cat and a dog'))
# ['cat', 'dog']

print(re.findall(r'colou?r|colour', 'color and colour'))
# ['color', 'colour']

Gruppi di cattura

Le parentesi () creano un gruppo di cattura. I gruppi ti permettono di estrarre sottoparte di una corrispondenza. re.search() restituisce un oggetto match; chiama .group(n) o .groups() su di esso.

import re

match = re.search(r'(\d{4})-(\d{2})-(\d{2})', '2023-10-05')
if match:
    print(match.group(0))   # '2023-10-05'  — entire match
    print(match.group(1))   # '2023'
    print(match.groups())   # ('2023', '10', '05')

Gruppi nominati

Assegna un nome a un gruppo con (?P<name>...) per accedervi tramite nome anziché posizione. Questo rende i pattern molto più facili da mantenere.

import re

match = re.search(
    r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})',
    '2023-10-05'
)
if match:
    print(match.group('year'))   # '2023'
    print(match.groupdict())     # {'year': '2023', 'month': '10', 'day': '05'}

Gruppi non-catturanti

Usa (?:...) quando hai bisogno di raggruppare ma non di catturare il valore.

import re

print(re.findall(r'(?:Mr|Mrs|Ms)\.? \w+', 'Mr. Smith and Mrs. Jones'))
# ['Mr. Smith', 'Mrs. Jones']

Lookahead e Lookbehind

Il lookahead ((?=...)) e il lookbehind ((?<=...)) asseriscono che qualcosa segua o preceda la corrispondenza, senza includerlo nel risultato. Sono zero-width: non consumano caratteri.

import re

# Positive lookahead — find numbers followed by 'dollars'
print(re.findall(r'\d+(?= dollars)', '100 dollars and 200 euros'))
# ['100']

# Negative lookahead — find numbers NOT followed by 'dollars'
print(re.findall(r'\b\d+\b(?! dollars)', '100 dollars and 200 euros'))
# ['200']

# Positive lookbehind — extract domain from email addresses
emails = 'Contact [email protected] or [email protected]'
print(re.findall(r'(?<=@)\w+\.\w+', emails))
# ['example.com', 'test.org']

I pattern lookbehind devono avere una larghezza fissa in Python — non puoi usare * o + al loro interno.

Il modulo re: funzioni principali

re.search() — Trova la prima corrispondenza

Restituisce un oggetto match per la prima posizione in cui il pattern corrisponde, oppure None se non c'è alcuna corrispondenza.

import re

text = 'apple banana apple'
match = re.search(r'banana', text)
if match:
    print(match.group())   # 'banana'
    print(match.span())    # (6, 12)

re.match() — Corrispondenza solo all'inizio

Come re.search(), ma il pattern deve corrispondere all'inizio della string.

import re

print(bool(re.match(r'\d+', 'abc123')))   # False — no digit at start
print(bool(re.search(r'\d+', 'abc123')))  # True  — digit found anywhere

Usa re.search() quando vuoi trovare un pattern ovunque; usa re.match() quando il pattern deve apparire all'inizio.

re.fullmatch() — Corrispondenza dell'intera string

Il pattern deve corrispondere all'intera string dall'inizio alla fine.

import re

print(bool(re.fullmatch(r'\d{5}', '12345')))   # True  — exactly 5 digits
print(bool(re.fullmatch(r'\d{5}', '123456')))  # False — too long
print(bool(re.fullmatch(r'\d{5}', '1234X')))   # False — non-digit present

re.fullmatch() è ideale per la validazione dell'input (codici postali, numeri di telefono, ecc.).

re.findall() — Tutte le corrispondenze non sovrapposte

Restituisce una lista di tutte le corrispondenze. Se il pattern ha gruppi, restituisce una lista di tuple.

import re

print(re.findall(r'\d+', 'abc 123 def 456'))
# ['123', '456']

# With a group — returns list of group values
print(re.findall(r'(\w+)@(\w+\.\w+)', '[email protected] [email protected]'))
# [('alice', 'example.com'), ('bob', 'test.org')]

re.finditer() — Iteratore di oggetti match

Come re.findall(), ma restituisce oggetti match uno alla volta. Utile per testi lunghi o quando hai bisogno delle informazioni sullo span.

import re

for m in re.finditer(r'\d+', 'abc 123 def 456'):
    print(m.group(), m.start(), m.end())
# 123 4 7
# 456 12 15

re.sub() — Sostituire le corrispondenze

Sostituisce ogni occorrenza del pattern con una string di sostituzione o il valore restituito da una funzione.

import re

text = 'apple banana apple'
print(re.sub(r'apple', 'orange', text))
# 'orange banana orange'

# Limit replacements
print(re.sub(r'apple', 'orange', text, count=1))
# 'orange banana apple'

# Backreferences in replacement — reformat a date
print(re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3/\2/\1', '2023-10-05'))
# '05/10/2023'

re.split() — Dividere per pattern

Divide la string a ogni occorrenza del pattern.

import re

print(re.split(r',\s*', 'apple, banana, cherry, date'))
# ['apple', 'banana', 'cherry', 'date']

# Limit the number of splits
print(re.split(r',\s*', 'apple, banana, cherry, date', maxsplit=2))
# ['apple', 'banana', 'cherry, date']

Compilare i pattern con re.compile()

Quando usi lo stesso pattern molte volte, compilalo una sola volta con re.compile() per migliorare le prestazioni. L'oggetto pattern risultante ha tutti gli stessi metodi (findall, search, sub, ecc.).

import re

# Compile once
digit_pattern = re.compile(r'\d+')

# Reuse many times
print(digit_pattern.findall('abc 123 def 456'))   # ['123', '456']
print(digit_pattern.sub('NUM', 'abc 123 def 456')) # 'abc NUM def NUM'
print(digit_pattern.search('xyz 99'))              # <re.Match object ...>

La compilazione è particolarmente vantaggiosa all'interno di cicli o funzioni chiamate frequentemente.

Flag

I flag modificano il comportamento della corrispondenza. Passali come ultimo argomento a qualsiasi funzione re, oppure includili quando chiami re.compile(). Più flag possono essere combinati con |.

FlagForma breveEffetto
re.IGNORECASEre.ICorrispondenza senza distinzione di maiuscole/minuscole
re.MULTILINEre.M^ e $ corrispondono all'inizio/fine di ogni riga
re.DOTALLre.S. corrisponde anche ai caratteri di a capo
re.VERBOSEre.XConsente spazi bianchi e commenti all'interno del pattern
import re

# IGNORECASE
print(re.findall(r'hello', 'Hello HELLO hello', re.IGNORECASE))
# ['Hello', 'HELLO', 'hello']

# MULTILINE — ^ matches the start of each line
text = 'first line\nsecond line\nthird line'
print(re.findall(r'^\w+', text, re.MULTILINE))
# ['first', 'second', 'third']

# DOTALL — . matches newline characters
print(re.findall(r'<.*?>', '<div>\n<p>text</p>\n</div>', re.DOTALL))
# ['<div>', '<p>', '</p>', '</div>']

# VERBOSE — write readable patterns with comments
date_pattern = re.compile(r'''
    (?P<year>\d{4})   # four-digit year
    -
    (?P<month>\d{2})  # two-digit month
    -
    (?P<day>\d{2})    # two-digit day
''', re.VERBOSE)
print(date_pattern.search('2023-10-05').groupdict())
# {'year': '2023', 'month': '10', 'day': '05'}

Escape dell'input utente con re.escape()

Se costruisci un pattern da testo fornito dall'utente, eseguine sempre l'escape per evitare un'interpretazione involontaria dei metacaratteri.

import re

user_input = 'hello.world'
# Without escaping, '.' matches any character
# With escaping, '\.' matches a literal dot
safe_pattern = re.escape(user_input)
print(safe_pattern)                                    # 'hello\\.world'
print(bool(re.search(safe_pattern, 'say hello.world')))  # True
print(bool(re.search(safe_pattern, 'say helloXworld')))  # False

Esempi pratici

Validare un indirizzo email

import re

def is_valid_email(email):
    pattern = r'^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$'
    return bool(re.fullmatch(pattern, email))

print(is_valid_email('[email protected]'))   # True
print(is_valid_email('bad-email@'))         # False

Estrarre tutti gli URL dal testo

import re

text = 'Visit https://www.example.com or http://test.org for details.'
urls = re.findall(r'https?://[\w./-]+', text)
print(urls)
# ['https://www.example.com', 'http://test.org']

Rimuovere gli spazi bianchi in eccesso

import re

messy = '  hello    world   Python  '
clean = re.sub(r'\s+', ' ', messy).strip()
print(clean)  # 'hello world Python'

Analizzare una riga di log

import re

log = '2023-10-05 14:32:11 ERROR Failed to connect to database'
pattern = re.compile(
    r'(?P<date>\d{4}-\d{2}-\d{2}) '
    r'(?P<time>\d{2}:\d{2}:\d{2}) '
    r'(?P<level>\w+) '
    r'(?P<message>.+)'
)
m = pattern.match(log)
if m:
    print(m.group('level'))    # 'ERROR'
    print(m.group('message'))  # 'Failed to connect to database'

Errori comuni

re.match() vs re.search()re.match() controlla solo l'inizio della string. I nuovi utenti spesso si aspettano che cerchi ovunque e rimangono confusi quando restituisce None.

Dimenticare le raw string'\d' in una normale string Python è semplicemente 'd' con un escape non riconosciuto (o un SyntaxWarning in Python 3.12+). Scrivi sempre r'\d'.

Corrispondenze greedy che consumano troppo — Se un pattern .* cattura più di quanto intendevi, passa a .*? (lazy).

Caratteri speciali nelle string di sostituzione — In re.sub(), le sequenze con backslash come \1 nella sostituzione si riferiscono ai gruppi catturati. Per includere un backslash letterale nella sostituzione, scrivi \\.

re.findall() con gruppi — Quando il tuo pattern contiene gruppi, re.findall() restituisce i gruppi, non la corrispondenza completa. Usa un gruppo non-catturante (?:...) se vuoi la corrispondenza completa.

Riferimento rapido

OperazioneFunzione
Trova prima corrispondenzare.search(pattern, text)
Trova tutte le corrispondenzere.findall(pattern, text)
Itera le corrispondenzere.finditer(pattern, text)
Corrispondenza all'iniziore.match(pattern, text)
Corrispondenza intera stringre.fullmatch(pattern, text)
Sostituiscire.sub(pattern, repl, text)
Dividire.split(pattern, text)
Compila per riutilizzore.compile(pattern)
Escape dell'input utentere.escape(text)

Capitoli correlati

  • Python Strings — metodi string che complementano la regex per compiti testuali più semplici.
  • Modify Strings — metodi integrati come replace() e split() che evitano la regex quando i pattern sono fissi.
  • Python File Handling — lettura di file riga per riga per applicare la regex su larga scala.
  • Python Try/Except — gestione degli errori che si verificano quando i pattern regex vengono compilati dall'input utente.
  • Python Functions — racchiudere la logica regex in funzioni riutilizzabili.

Esercitati

Pratica
What does the 're' module in Python provide?
What does the 're' module in Python provide?
Was this page helpful?