Lezione 3: Tecniche avanzate di manipolazione delle stringhe

CORSO PYTHON

La manipolazione delle stringhe è una competenza fondamentale per qualsiasi sviluppatore che utilizza Python. Non solo è essenziale per lo sviluppo di software su larga scala, ma è anche cruciale in campi come la data science, l’intelligenza artificiale e altri settori tecnologici avanzati. In questa sezione, esploreremo le operazioni di base con le stringhe, evidenziando dettagli e informazioni chiave che ogni programmatore dovrebbe conoscere.

Le operazioni di base con le stringhe in Python includono la concatenazione, la ripetizione, lo slicing e l’interpolazione.

Concatenazione di stringhe avviene tramite l’operatore +, permettendo di unire facilmente due o più stringhe:

stringa1 = "Ciao, "
stringa2 = "mondo!"
risultato = stringa1 + stringa2
print(risultato)  # Output: Ciao, mondo!

Ripetizione delle stringhe usando l’operatore *:

stringa = "Python "
risultato = stringa * 3
print(risultato)  # Output: Python Python Python 

Slicing per estrarre porzioni di una stringa utilizzando l’operatore []:

stringa = "Hello, World!"
sottostringa = stringa[7:12]
print(sottostringa)  # Output: World

Introduzione delle f-string (formatted string) in Python 3.6 per una formattazione più chiara delle stringhe:

nome = "Alice"
eta = 30
messaggio = f"Ciao, mi chiamo {nome} e ho {eta} anni."
print(messaggio)  # Output: Ciao, mi chiamo Alice e ho 30 anni.

Uso avanzato di metodi e funzioni built-in per la manipolazione delle stringhe

Padroneggiare metodi e funzioni built-in avanzate può migliorare notevolmente la qualità e l’efficienza del codice. In questa sezione, ci concentreremo su tecniche avanzate per la manipolazione delle stringhe, fornendo dettagli ed esempi pratici.

Metodi Built-in Avanzati

Python offre metodi come strip(), split(), join(), replace() e format() per lavorare con le stringhe.

Il metodo strip() rimuove spazi bianchi o caratteri specifici all’inizio e alla fine di una stringa:

stringa = "**Hello, World!**"
risultato = stringa.strip("*")
print(risultato)  # Output: Hello, World!

Il metodo split() divide una stringa in una lista, basandosi su un separatore:

testo = "apple,banana,cherry,dates"
frutta = testo.split(",")
print(frutta)  # Output: ['apple', 'banana', 'cherry', 'dates']

Il metodo join() unisce una lista di stringhe in una singola stringa con un delimitatore:

frutta = ['apple', 'banana', 'cherry', 'dates']
delimitatore = " - "
frutta_stringa = delimitatore.join(frutta)
print(frutta_stringa)  # Output: apple - banana - cherry - dates

Utilizzo di replace() e format()

Il metodo replace() sostituisce occorrenze di una sottostringa con un’altra:

testo = "Il prezzo è di $100"
nuovo_testo = testo.replace("$", "€")
print(nuovo_testo)  # Output: Il prezzo è di €100

Il metodo format() per la formattazione delle stringhe:

nome = "Bob"
eta = 25
messaggio = "Ciao, mi chiamo {} e ho {} anni.".format(nome, eta)
print(messaggio)  # Output: Ciao, mi chiamo Bob e ho 25 anni.

Espressioni regolari (regex) in Python

Le espressioni regolari, comunemente note come regex, sono strumenti potenti e versatili per la manipolazione avanzata delle stringhe in Python. Utilizzando il modulo re, è possibile effettuare operazioni complesse come ricerca, sostituzione e divisione basate su pattern specifici.

Esempio di estrazione di tutte le parole da una stringa:

import re

testo = "Hello, World! Benvenuti nel mondo di Python."
parole = re.findall(r'\b\w+\b', testo)
print(parole)  # Output: ['Hello', 'World', 'Benvenuti', 'nel', 'mondo', 'di', 'Python']

In questo esempio, l’espressione regolare r'\b\w+\b' viene utilizzata per trovare tutte le parole nella stringa. Ecco come funziona:

  • \b indica un confine di parola (inizio o fine di una parola).
  • \w+ corrisponde a uno o più caratteri alfanumerici (una parola).
  • Combinando questi elementi, l’espressione cerca sequenze di caratteri alfanumerici delimitate da confini di parola.

Uso di quantificatori non greedy:

import re

testo = "<div><p>Paragrafo</p></div>"
match = re.search(r'<.*?>', testo)
print(match.group())  # Output: <div>

In questo esempio, vogliamo estrarre il primo tag HTML dalla stringa. L’espressione regolare r'<.*?>' viene utilizzata per raggiungere questo obiettivo. Vediamo come funziona:

  • < e > sono caratteri letterali che rappresentano l’inizio e la fine di un tag HTML.
  • .*? è un quantificatore non greedy:
    • . corrisponde a qualsiasi carattere (tranne il carattere di nuova linea per impostazione predefinita).
    • * indica “zero o più occorrenze” del carattere precedente.
    • ? rende il quantificatore non greedy, cioè cerca il match più breve possibile.

Senza il quantificatore non greedy, usando r'<.*>', l’espressione regolare sarebbe greedy e abbinerebbe il match più lungo possibile:

match = re.search(r'<.*>', testo)
print(match.group())  # Output: <div><p>Paragrafo</p></div>

Come si può vedere, l’espressione greedy cattura tutto dal primo < all’ultimo >, includendo tutto il contenuto interno. Questo potrebbe non essere desiderato se si vuole estrarre solo il primo tag.

Utilizzando il quantificatore non greedy *?, l’espressione si ferma al primo match che soddisfa il pattern, cioè il primo <div>.

Esempio di utilizzo con re.findall():

tags = re.findall(r'<.*?>', testo)
print(tags)  # Output: ['<div>', '<p>', '</p>', '</div>']

Qui, l’espressione regolare con il quantificatore non greedy permette di trovare tutti i tag HTML nella stringa, poiché si ferma dopo ogni tag individuale invece di estendersi al match più lungo possibile.

Quantificatori greedy (golosi) tentano di abbinare il maggior numero possibile di caratteri. Nel caso di .*, l’espressione corrisponde a qualsiasi carattere ripetuto zero o più volte, espandendosi il più possibile all’interno dei limiti definiti.

Quantificatori non greedy (non golosi), come .*?, cercano di abbinare il minor numero possibile di caratteri necessari per soddisfare l’espressione regolare.

Capire la differenza tra questi due comportamenti è fondamentale quando si lavora con espressioni regolari, specialmente in casi in cui il testo contiene pattern ripetuti o nidificati, come nei documenti HTML o XML.

Ulteriori esempi di quantificatori non greedy

Supponiamo di avere la seguente stringa:

testo = "Inizio <tag1> contenuto <tag2> fine"

Utilizzando un quantificatore greedy:

match = re.search(r'<.*>', testo)
print(match.group())  # Output: <tag1> contenuto <tag2>

Il match include tutto dal primo < all’ultimo > prima della parola “fine”. Se invece utilizziamo un quantificatore non greedy:

match = re.search(r'<.*?>', testo)
print(match.group())  # Output: <tag1>

Ora, il match si ferma al primo </>, catturando solo <tag1>.

Quando utilizzare i quantificatori non greedy

I quantificatori non greedy sono particolarmente utili quando si desidera:

  • Estrarre elementi specifici da un testo senza includere contenuti aggiuntivi.
  • Evitare che l’espressione regolare catturi più di quanto necessario, soprattutto in presenza di pattern ripetuti o nidificati.
  • Analizzare linguaggi di markup come HTML o XML, dove i tag possono essere annidati.

Nota: L’uso corretto dei quantificatori migliora l’efficienza delle espressioni regolari e riduce il rischio di risultati imprevisti.

Uso di lookahead per validare una password che deve contenere almeno una cifra e un carattere speciale:

password = "Password123!"
pattern = r'^(?=.*\d)(?=.*[@$!%*?&]).{8,}$'
if re.match(pattern, password):
    print("Password valida")
else:
    print("Password non valida")

Esercizi pratici sulla manipolazione avanzata delle stringhe

Per rafforzare le competenze acquisite, è essenziale svolgere esercizi pratici che mettano in luce tecniche sia teoriche che applicative.

Esercizio 1: Validazione di indirizzi email

Crea una funzione che verifichi se una stringa è un indirizzo email valido utilizzando espressioni regolari.

import re

def email_valida(email):
    pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
    return re.match(pattern, email) is not None

# Test
print(email_valida("esempio@example.com"))  # Output: True
print(email_valida("esempio@com"))          # Output: False

Esercizio 2: Parsing di file CSV

Leggi un file CSV e utilizza i metodi di manipolazione delle stringhe per estrarre e formattare i dati.

with open('dati.csv', 'r') as file:
    for linea in file:
        campi = linea.strip().split(',')
        print(campi)

Esercizio 3: Serializzazione e Deserializzazione JSON

Utilizza il modulo json per convertire un dizionario Python in una stringa JSON e viceversa.

import json

# Serializzazione
dati = {'nome': 'Alice', 'eta': 30, 'citta': 'Roma'}
json_string = json.dumps(dati)
print(json_string)  # Output: {"nome": "Alice", "eta": 30, "citta": "Roma"}

# Deserializzazione
dati_caricati = json.loads(json_string)
print(dati_caricati['nome'])  # Output: Alice

 

Se vuoi farmi qualche richiesta o contattarmi per un aiuto riempi il seguente form