Python - Regular Expressions (Italiano)
Ciao là, futuri maghi Python! Oggi, intraprenderemo un viaggio avventuroso nel mondo delle Espressioni Regolari (regex) in Python. Non preoccuparti se non hai mai sentito parlare di regex prima: alla fine di questo tutorial, sarai in grado di utilizzare questo potente strumento come un professionista!
Cos'sono le Espressioni Regolari?
Prima di immergerci, capiamo cos'sono le espressioni regolari. Immagina di essere un detective che cerca un pattern specifico in un mare di testo. Le espressioni regolari sono come la tua lente di ingrandimento, aiutandoti a cercare e manipolare stringhe in base a pattern. Cool, vero?
Stringhe Raw
In Python, quando lavori con regex, spesso utilizzi stringhe raw. Queste sono precedute da un 'r' e trattano i backslash come caratteri letterali. Questo è particolarmente utile nelle regex, dato che i backslash sono comuni.
# Stringa normale
print("Ciao\nMondo")
# Stringa raw
print(r"Ciao\nMondo")
Nel primo caso, vedrai "Ciao" e "Mondo" su righe separate. Nel secondo, vedrai "Ciao\nMondo" così com'è. Questo diventa cruciale quando si lavora con pattern regex.
Metacaratteri
I metacaratteri sono i mattoni fondamentali delle regex. Hanno un significato speciale e ci aiutano a definire i pattern. Ecco alcuni comuni:
Metacarattere | Significato |
---|---|
. | Corrisponde a qualsiasi carattere tranne il newline |
^ | Corrisponde all'inizio di una stringa |
$ | Corrisponde alla fine di una stringa |
* | Corrisponde a 0 o più ripetizioni |
+ | Corrisponde a 1 o più ripetizioni |
? | Corrisponde a 0 o 1 ripetizione |
{} | Corrisponde a un numero specificato di ripetizioni |
[] | Specifica un set di caratteri da corrispondere |
\ | Esca i caratteri speciali |
La funzione re.match()
La funzione re.match()
tenta di trovare una corrispondenza all'inizio di una stringa. Se trova una corrispondenza, restituisce un oggetto di corrispondenza; altrimenti, restituisce None.
import re
risultato = re.match(r"Ciao", "Ciao, Mondo!")
if risultato:
print("Corrispondenza trovata:", risultato.group())
else:
print("Nessuna corrispondenza")
Questo stampa "Corrispondenza trovata: Ciao". Il metodo group()
restituisce la sottostringa corrispondente.
La funzione re.search()
Mentre re.match()
cerca una corrispondenza all'inizio di una stringa, re.search()
scansiona l'intera stringa per trovare una corrispondenza.
import re
risultato = re.search(r"Mondo", "Ciao, Mondo!")
if risultato:
print("Corrispondenza trovata:", risultato.group())
else:
print("Nessuna corrispondenza")
Questo stampa "Corrispondenza trovata: Mondo".
Confronto tra Match e Search
La principale differenza tra match()
e search()
è che match()
cerca una corrispondenza solo all'inizio della stringa, mentre search()
cerca una corrispondenza ovunque nella stringa.
La funzione re.findall()
La funzione re.findall()
restituisce tutte le corrispondenze non sovrapposte di un pattern in una stringa come una lista.
import re
testo = "La pioggia in Spagna cade principalmente nella pianura"
risultato = re.findall(r"ain", testo)
print(risultato)
Questo stampa ['ain', 'ain', 'ain']
.
La funzione re.sub()
La funzione re.sub()
sostituisce tutte le occorrenze di un pattern in una stringa con una stringa di sostituzione.
import re
testo = "La pioggia in Spagna"
risultato = re.sub(r"a", "o", testo)
print(risultato)
Questo stampa "La piooggio in Spogna".
La funzione re.compile()
La funzione re.compile()
crea un oggetto regex per il riutilizzo, che può essere più efficiente se stai usando lo stesso pattern più volte.
import re
pattern = re.compile(r"\d+")
risultato1 = pattern.findall("Ci sono 123 mele e 456 arance")
risultato2 = pattern.findall("Ho 789 banane")
print(risultato1)
print(risultato2)
Questo stampa ['123', '456']
e ['789']
.
La funzione re.finditer()
La funzione re.finditer()
restituisce un iteratore che produce oggetti di corrispondenza per tutte le corrispondenze non sovrapposte di un pattern in una stringa.
import re
testo = "La pioggia in Spagna"
for corrispondenza in re.finditer(r"ain", testo):
print(f"Trovato '{corrispondenza.group()}' alla posizione {corrispondenza.start()}-{corrispondenza.end()}")
Questo stampa:
Trovato 'ain' alla posizione 5-8
Trovato 'ain' alla posizione 17-20
Casi d'uso delle Regex in Python
Le espressioni regolari hanno numerose applicazioni pratiche. Guardiamo un caso d'uso comune:
Trovare parole che iniziano con vocali
import re
testo = "Un'aquila al giorno tira il dottore giù"
parole_con_vocali = re.findall(r'\b[aeiouAEIOU]\w+', testo)
print(parole_con_vocali)
Questo stampa ['Un'aquila', 'al', 'a', 'giù']
.
Modificatori delle Espressioni Regolari: Opzioni di Flag
Il modulo re di Python fornisce diversi flag di opzione che modificano come i pattern sono interpretati:
Flag | Descrizione |
---|---|
re.IGNORECASE (re.I) | Esegue una corrispondenza case-insensitive |
re.MULTILINE (re.M) | Fa sì che ^ corrisponda all'inizio di ogni riga e $ alla fine di ogni riga |
re.DOTALL (re.S) | Fa sì che . corrisponda a qualsiasi carattere, inclusi newline |
re.VERBOSE (re.X) | Permette di scrivere pattern regex più leggibili |
Pattern delle Espressioni Regolari
Esploriamo alcuni pattern più avanzati:
Classi di caratteri
Le classi di caratteri permettono di specificare un set di caratteri da corrispondere:
import re
testo = "Il volatile brown fox salta sopra il cane pigro"
risultato = re.findall(r"[aeiou]", testo)
print(risultato)
Questo stampa tutte le vocali trovate nel testo.
Classi di Caratteri Speciali
Python regex supporta classi di caratteri speciali:
Classe | Descrizione |
---|---|
\d | Corrisponde a qualsiasi cifra decimale |
\D | Corrisponde a qualsiasi carattere non numerico |
\s | Corrisponde a qualsiasi carattere di spaziatura |
\S | Corrisponde a qualsiasi carattere non di spaziatura |
\w | Corrisponde a qualsiasi carattere alfanumerico |
\W | Corrisponde a qualsiasi carattere non alfanumerico |
Casi di Ripetizione
Possiamo specificare quante volte un pattern dovrebbe comparire:
import re
testo = "Ho 111 mele e 22 arance"
risultato = re.findall(r"\d{2,3}", testo)
print(risultato)
Questo stampa ['111', '22']
, corrispondendo a numeri con 2 o 3 cifre.
Ripetizione non greedy
Per default, la ripetizione è greedy, ossia corrisponde quante più volte possibile. Aggiungendo un ? dopo la ripetizione la rende non greedy:
import re
testo = "<h1>Titolo</h1><p>Paragrafo</p>"
greedy = re.findall(r"<.*>", testo)
non_greedy = re.findall(r"<.*?>", testo)
print("Greedy:", greedy)
print("Non-greedy:", non_greedy)
Questo mostrerà la differenza tra matching greedy e non greedy.
Gruppo con Parentesi
Le parentesi permettono di raggruppare parti della regex:
import re
testo = "John Smith ([email protected])"
risultato = re.search(r"(\w+) (\w+) \((\w+@\w+\.\w+)\)", testo)
if risultato:
print(f"Nome Completo: {risultato.group(1)} {risultato.group(2)}")
print(f"Email: {risultato.group(3)}")
Questo estrae il nome e l'email dal testo.
Backreference
Le backreference permettono di fare riferimento a gruppi corrispondenti precedentemente:
import re
testo = "<h1>Titolo</h1><p>Paragrafo</p>"
risultato = re.findall(r"<(\w+)>.*?</\1>", testo)
print(risultato)
Questo corrisponde a tag HTML di apertura e chiusura.
Alternative
Il carattere | permette di specificare alternative:
import re
testo = "Il colore del cielo è blu o grigio"
risultato = re.search(r"blu|grigio", testo)
if risultato:
print(f"Trovato colore: {risultato.group()}")
Questo corrisponde a "blu" o "grigio".
Ancore
Le ancore specificano posizioni nel testo:
import re
testo = "Python è fantastico"
inizio = re.match(r"^Python", testo)
fine = re.search(r"fantastico$", testo)
print(f"Comincia con Python: {bool(inizio)}")
print(f"Termina con fantastico: {bool(fine)}")
Questo verifica se il testo inizia con "Python" e finisce con "fantastico".
Sintassi Speciale con Parentesi
Le parentesi possono essere usate per più che solo raggruppare:
- (?:...) crea un gruppo non catturante
- (?P
...) crea un gruppo con nome - (?=...) crea un lookahead positivo
- (?!...) crea un lookahead negativo
import re
testo = "Python versione 3.9.5"
risultato = re.search(r"Python (?:versione )?(?P<versione>\d+\.\d+\.\d+)", testo)
if risultato:
print(f"Versione: {risultato.group('versione')}")
Questo estrae il numero di versione, indipendentemente dal fatto che "versione" sia presente o meno nel testo.
Eccoci qua, ragazzi! Abbiamo viaggiato attraverso la terra delle regex in Python, dai concetti di base ai concetti più avanzati. Ricorda, come ogni strumento potente, le regex richiedono pratica per essere padroneggiate. Quindi, non essere scoraggiato se all'inizio sembra complicato. Continua a sperimentare, e presto sarai in grado di trovare pattern come un detective professionista! Buon coding!
Credits: Image by storyset