Introduzione
Quando si lavora con stringhe in Python, spesso non basta fare semplici sostituzioni o usare strip() e lower(). Nei progetti reali, i dati testuali arrivano quasi sempre “sporchi”: possono contenere spazi extra, caratteri invisibili, accenti, maiuscole incoerenti, simboli non desiderati o formati diversi per lo stesso contenuto.
Un sotto-argomento molto utile, pratico e spesso sottovalutato è la normalizzazione avanzata delle stringhe. Questo processo serve a rendere testi diversi tra loro più uniformi, così da confrontarli, cercarli, salvarli o validarli in modo affidabile.
In questo tutorial vedremo come costruire una piccola utility Python per:
- rimuovere spazi e caratteri invisibili;
- uniformare maiuscole e minuscole;
- gestire accenti e segni diacritici;
- trasformare testi in formato “slug” adatto a URL o identificatori;
- confrontare stringhe in modo più robusto.
Questo approccio è utile in contesti come importazione dati, ricerca testuale, pulizia di input utente, deduplicazione di record e preparazione di contenuti per database o API.
Codice completo
import unicodedata
import re
def rimuovi_caratteri_invisibili(testo: str) -> str:
"""
Rimuove caratteri invisibili comuni come:
- zero-width space
- BOM
- caratteri di controllo non stampabili
"""
if testo is None:
return ""
# Normalizza eventuali caratteri Unicode equivalenti
testo = unicodedata.normalize("NFKC", testo)
# Elimina caratteri di controllo e invisibili
caratteri_da_rimuovere = [
"u200b", # zero width space
"u200c", # zero width non-joiner
"u200d", # zero width joiner
"ufeff", # byte order mark
]
for char in caratteri_da_rimuovere:
testo = testo.replace(char, "")
# Rimuove anche altri caratteri di controllo ASCII
testo = "".join(ch for ch in testo if unicodedata.category(ch)[0] != "C")
return testo
def normalizza_spazi(testo: str) -> str:
"""
Converte sequenze di spazi, tab e a capo in un singolo spazio.
Rimuove gli spazi iniziali e finali.
"""
if testo is None:
return ""
testo = rimuovi_caratteri_invisibili(testo)
# Sostituisce qualsiasi sequenza di whitespace con un singolo spazio
testo = re.sub(r"s+", " ", testo)
return testo.strip()
def rimuovi_accenti(testo: str) -> str:
"""
Converte caratteri accentati in equivalenti ASCII quando possibile.
Esempio: ´città´ -> ´citta´
"""
if testo is None:
return ""
testo = unicodedata.normalize("NFD", testo)
testo = "".join(
ch for ch in testo
if unicodedata.category(ch) != "Mn"
)
return unicodedata.normalize("NFC", testo)
def normalizza_testo(testo: str, minuscolo: bool = True, senza_accenti: bool = False) -> str:
"""
Pipeline di normalizzazione:
- pulizia caratteri invisibili
- normalizzazione spazi
- conversione in minuscolo
- rimozione accenti opzionale
"""
testo = normalizza_spazi(testo)
if minuscolo:
testo = testo.lower()
if senza_accenti:
testo = rimuovi_accenti(testo)
return testo
def slugify(testo: str) -> str:
"""
Converte una stringa in uno slug semplice:
- minuscolo
- senza accenti
- solo lettere, numeri e trattini
- spazi convertiti in trattini
"""
testo = normalizza_testo(testo, minuscolo=True, senza_accenti=True)
# Mantiene solo lettere, numeri, spazi e trattini
testo = re.sub(r"[^a-z0-9s-]", "", testo)
# Converte sequenze di spazi o trattini in un solo trattino
testo = re.sub(r"[s-]+", "-", testo)
return testo.strip("-")
def confronta_stringhe(a: str, b: str) -> bool:
"""
Confronta due stringhe in modo robusto dopo normalizzazione.
Utile per deduplicazione o matching di input utente.
"""
return normalizza_testo(a, senza_accenti=True) == normalizza_testo(b, senza_accenti=True)
# -------------------------
# Esempi di utilizzo
# -------------------------
testi = [
" Città di Milano ",
"Cittàu200b di Milano",
"CITTA DI MILANO",
"Città-di-Milano!!!",
]
for t in testi:
print("ORIGINALE:", repr(t))
print("NORMALIZZATO:", normalizza_testo(t, senza_accenti=True))
print("SLUG:", slugify(t))
print("-" * 40)
print(confronta_stringhe("Città di Milano", "citta di milano"))
print(confronta_stringhe("Pippo", "Pluto")) Spiegazione
Il codice è organizzato come una piccola “pipeline” di pulizia. Questo è un approccio molto utile perché rende il comportamento chiaro, riutilizzabile e facile da testare.
1. Rimozione dei caratteri invisibili
La funzione rimuovi_caratteri_invisibili() elimina simboli che spesso causano bug difficili da individuare. Un esempio classico è il carattere zero-width space, che sembra non esistere ma altera confronti e ricerche.
In più, la funzione usa unicodedata.category(ch) per scartare caratteri di controllo, cioè quelli appartenenti alla categoria Unicode che inizia con C. Questo aiuta a evitare problemi con input provenienti da copincolla, file esterni o sistemi diversi.
2. Normalizzazione degli spazi
Con normalizza_spazi() convertiamo qualsiasi sequenza di spazi, tab o a capo in un solo spazio. Questo è utile quando il testo proviene da moduli web, OCR, PDF estratti o campi compilati manualmente.
Per farlo usiamo una regex semplice: s+. È una soluzione pratica e leggibile per collassare il whitespace.
3. Rimozione degli accenti
La funzione rimuovi_accenti() usa la normalizzazione Unicode in forma NFD, che separa lettere e segni diacritici. Poi elimina i caratteri con categoria Mn, cioè i segni non combinanti.
Per esempio:
- città diventa citta
- è diventa e
- à diventa a
Questa tecnica è molto utile per confronti “tolleranti”, ma va usata con attenzione: in alcuni contesti gli accenti sono informazione importante e non andrebbero rimossi.
4. Pipeline di normalizzazione
normalizza_testo() combina più passaggi in un’unica funzione. Questo è un buon esempio di design pulito: invece di spargere logica di pulizia in vari punti del programma, centralizziamo tutto in un solo posto.
Il parametro minuscolo permette di decidere se convertire il testo in lowercase, mentre senza_accenti rende la funzione flessibile in base al caso d’uso.
5. Creazione di uno slug
La funzione slugify() trasforma una stringa in un formato adatto a URL, identificatori o nomi file. Per esempio:
- "Città di Milano" → "citta-di-milano"
- "Città-di-Milano!!!" → "citta-di-milano"
Questo è molto utile per blog, CMS, e-commerce e sistemi di routing web.
6. Confronto robusto tra stringhe
La funzione confronta_stringhe() mostra un caso d’uso concreto: due stringhe che sembrano diverse possono rappresentare lo stesso contenuto. Dopo la normalizzazione, "Città di Milano" e "citta di milano" diventano equivalenti.
Questo è molto utile per deduplicazione di dati, matching di nomi, confronto di etichette e validazione di input.
Best practice
- Separare i passaggi di pulizia: funzioni piccole e specializzate sono più facili da testare e riusare.
- Usare Unicode consapevolmente: la gestione dei caratteri internazionali non va trattata come semplice testo ASCII.
- Non rimuovere gli accenti per default: fallo solo se il caso d’uso lo richiede davvero.
- Centralizzare la normalizzazione: evita di ripetere la stessa logica in più punti del progetto.
- Testare con input reali: prova stringhe con spazi multipli, caratteri invisibili, emoji, apostrofi, trattini e testi multilingua.
- Preferire
casefold()quando serve confronto internazionale: in alcuni casi è più adatto dilower()per confronti case-insensitive più robusti.
Un errore comune è “pulire troppo”: ad esempio, rimuovere simboli o accenti senza valutare l’impatto semantico. La normalizzazione deve essere sempre guidata dal contesto.
Riepilogo
La normalizzazione avanzata delle stringhe è una competenza molto pratica in Python. Ti permette di trasformare testi inconsistenti in dati affidabili, confrontabili e riutilizzabili. Nel tutorial abbiamo visto come:
- eliminare caratteri invisibili e di controllo;
- uniformare gli spazi;
- rimuovere accenti quando serve;
- costruire una pipeline di pulizia;
- generare slug puliti;
- confrontare stringhe in modo robusto.
Queste tecniche sono fondamentali in applicazioni reali dove il testo arriva da fonti diverse e non sempre affidabili. Investire in una buona normalizzazione significa ridurre bug, migliorare la qualità dei dati e semplificare il lavoro del resto del programma.
Approfondisci con risorse ufficiali
- Python Standard Library - unicodedata: documentazione ufficiale per la gestione delle proprietà Unicode.
- Python Standard Library - re: modulo ufficiale per le espressioni regolari.
- Python String Methods: elenco completo dei metodi nativi delle stringhe.
- Unicode Standard: riferimento per capire categorie, normalizzazioni e proprietà dei caratteri.
Se vuoi fare un passo in più, il prossimo argomento utile è costruire una pipeline di validazione testo + normalizzazione + confronto fuzzy, molto usata in motori di ricerca, CRM e sistemi di importazione dati.
