6. Gestione pratica delle stringhe
Nel Capitolo 5 hai imparato le basi del lavoro con le stringhe: come crearle, accedere ai caratteri tramite indicizzazione e slicing, e usare i metodi fondamentali per le stringhe. Ora costruiremo su quelle basi per esplorare operazioni sulle stringhe più sofisticate che userai continuamente nei veri programmi Python.
Questo capitolo si concentra su tecniche pratiche di manipolazione delle stringhe che risolvono problemi di programmazione quotidiani: spezzare il testo e rimetterlo insieme, creare output formattato dall'aspetto professionale. Queste abilità sono essenziali sia che tu stia elaborando input dell'utente, generando report, leggendo file di dati, o costruendo qualsiasi programma che lavori con il testo.
6.1) Suddividere e unire stringhe
Uno dei compiti più comuni nell'elaborazione del testo è suddividere una stringa in parti più piccole oppure combinare più parti in un'unica stringa. Python fornisce metodi potenti per entrambe le operazioni.
6.1.1) Suddividere le stringhe con split()
Il metodo split() suddivide una stringa in una lista (list) di stringhe più piccole in base a un separatore (chiamato anche delimitatore). Questo è estremamente utile per elaborare testo strutturato come dati CSV, input dell'utente con più valori, o frasi suddivise in parole.
Suddivisione di base per spazi bianchi:
Quando chiami split() senza argomenti, la stringa viene suddivisa su qualsiasi spazio bianco (spazi, tabulazioni, newline) e le stringhe vuote vengono automaticamente rimosse dal risultato:
# split_basic.py
sentence = "Python is a powerful programming language"
words = sentence.split()
print(words) # Output: ['Python', 'is', 'a', 'powerful', 'programming', 'language']
print(len(words)) # Output: 6Nota come split() gestisce in modo intelligente più spazi:
# split_whitespace.py
messy_text = "Python is awesome"
words = messy_text.split()
print(words) # Output: ['Python', 'is', 'awesome']Anche se ci sono più spazi tra le parole, split() tratta qualsiasi quantità di spazio bianco come un singolo separatore e produce una lista pulita.
Suddividere con un separatore specifico:
Puoi specificare esattamente quale carattere o stringa usare come separatore passandolo come argomento:
# split_separator.py
csv_data = "apple,banana,cherry,date"
fruits = csv_data.split(',')
print(fruits) # Output: ['apple', 'banana', 'cherry', 'date']
date_string = "2024-03-15"
parts = date_string.split('-')
print(parts) # Output: ['2024', '03', '15']
year = parts[0]
month = parts[1]
day = parts[2]
print(f"Year: {year}, Month: {month}, Day: {day}") # Output: Year: 2024, Month: 03, Day: 15Differenza importante: Quando specifichi un separatore, split() lo tratta letteralmente e creerà stringhe vuote se il separatore compare consecutivamente:
# split_empty_strings.py
data = "apple,,cherry"
result = data.split(',')
print(result) # Output: ['apple', '', 'cherry']
print(len(result)) # Output: 3La stringa vuota '' al centro rappresenta il "nulla" tra le due virgole consecutive. Questo comportamento è diverso dal caso in cui si suddivide sugli spazi bianchi senza argomenti.
Limitare il numero di suddivisioni:
Puoi controllare quante suddivisioni avvengono passando un secondo argomento (maxsplit):
# split_maxsplit.py
text = "one:two:three:four:five"
parts = text.split(':', 2) # Suddivide solo sulle prime 2 virgole
print(parts) # Output: ['one', 'two', 'three:four:five']Questo crea al massimo 3 parti (maxsplit + 1) perché si ferma dopo il numero specificato di suddivisioni. Il resto della stringa rimane intatto.
Esempio pratico: elaborare input dell'utente
# process_input.py
user_input = input("Enter your full name: ")
# L'utente inserisce: "Alice Marie Johnson"
name_parts = user_input.split()
if len(name_parts) >= 2:
first_name = name_parts[0]
last_name = name_parts[-1] # Ultimo elemento
print(f"First name: {first_name}") # Output: First name: Alice
print(f"Last name: {last_name}") # Output: Last name: Johnson
else:
print("Please enter at least a first and last name")6.1.2) Unire stringhe con join()
Il metodo join() è l'opposto di split(): combina una lista (o qualsiasi iterable) di stringhe in un'unica stringa, con un separatore tra ogni elemento. La sintassi può sembrare invertita all'inizio: è la stringa separatrice che chiama il metodo, e la lista viene passata come argomento.
Unione di base:
# join_basic.py
words = ['Python', 'is', 'awesome']
sentence = ' '.join(words)
print(sentence) # Output: Python is awesome
csv_line = ','.join(['apple', 'banana', 'cherry'])
print(csv_line) # Output: apple,banana,cherryLa stringa che chiama join() (come ' ' o ',') diventa il separatore tra gli elementi.
Perché la sintassi è separator.join(list):
Questa sintassi ha senso se la guardi dalla prospettiva del separatore: "Voglio unire questi elementi inserendo me stesso tra ogni coppia." Permette anche concatenazioni eleganti (chaining) e rende il separatore molto visibile nel codice.
Unire con separatori diversi:
# join_separators.py
items = ['eggs', 'milk', 'bread', 'butter']
# Separati da virgola
print(', '.join(items)) # Output: eggs, milk, bread, butter
# Separati da newline (ogni elemento su una riga)
print('\n'.join(items))
# Output:
# eggs
# milk
# bread
# butter
# Separati da trattino
print('-'.join(items)) # Output: eggs-milk-bread-butter
# Nessun separatore (concatenazione)
print(''.join(items)) # Output: eggsmilkbreadbutterImportante: join() funziona solo con stringhe:
Tutti gli elementi dell'iterable devono essere stringhe. Se provi a unire numeri o altri tipi, otterrai un errore:
# join_error.py
numbers = [1, 2, 3, 4]
# result = ','.join(numbers) # This would cause: TypeError: sequence item 0: expected str instance, int foundPer unire elementi che non sono stringhe, convertili prima in stringhe. Impareremo un modo più elegante per farlo nel Capitolo 34, ma per ora puoi convertire manualmente ogni elemento:
# join_numbers.py
numbers = [1, 2, 3, 4]
# Converte manualmente ogni numero in stringa
string_numbers = [str(numbers[0]), str(numbers[1]), str(numbers[2]), str(numbers[3])]
result = ','.join(string_numbers)
print(result) # Output: 1,2,3,4Esempio pratico: costruire percorsi di file
# build_path.py
path_parts = ['home', 'user', 'documents', 'report.txt']
# Su sistemi Unix-like (Linux, macOS)
unix_path = '/'.join(path_parts)
print(unix_path) # Output: home/user/documents/report.txt
# Su Windows
windows_path = '\\'.join(path_parts)
print(windows_path) # Output: home\user\documents\report.txtNota: Nel Capitolo 26 impareremo il modulo os.path, che fornisce una gestione dei percorsi multipiattaforma migliore, ma questo esempio mostra il concetto di unione.
6.1.3) Combinare split() e join() per l'elaborazione del testo
Questi due metodi funzionano benissimo insieme per trasformare il testo. Combinandoli, puoi ripulire input disordinato, convertire tra formati diversi, oppure estrarre e riorganizzare dati:
# transform_text.py
# Sostituisce più spazi con un singolo spazio
messy = "Python is really cool"
clean = ' '.join(messy.split())
print(clean) # Output: Python is really cool
# Converte una stringa separata da virgole in una separata da spazi
csv_data = "apple,banana,cherry"
space_separated = ' '.join(csv_data.split(','))
print(space_separated) # Output: apple banana cherry
# Rimuove tutti gli spazi
text_with_spaces = "H e l l o"
no_spaces = ''.join(text_with_spaces.split())
print(no_spaces) # Output: Hello6.1.4) Altri metodi di suddivisione
Python fornisce metodi di suddivisione aggiuntivi per casi d'uso specifici:
rsplit() - Suddividere da destra:
# rsplit_example.py
path = "folder/subfolder/file.txt"
# split regolare con maxsplit
parts = path.split('/', 1)
print(parts) # Output: ['folder', 'subfolder/file.txt']
# rsplit suddivide da destra
parts = path.rsplit('/', 1)
print(parts) # Output: ['folder/subfolder', 'file.txt']Questo è utile quando vuoi separare l'ultima parte di una stringa da tutto ciò che la precede.
splitlines() - Suddividere sulle interruzioni di riga:
# splitlines_example.py
multiline = "Line 1\nLine 2\nLine 3"
lines = multiline.splitlines()
print(lines) # Output: ['Line 1', 'Line 2', 'Line 3']
# Funziona con diversi stili di terminazione di riga
mixed_lines = "Line 1\nLine 2\r\nLine 3\rLine 4"
all_lines = mixed_lines.splitlines()
print(all_lines) # Output: ['Line 1', 'Line 2', 'Line 3', 'Line 4']Il metodo splitlines() riconosce tutte le convenzioni standard per le interruzioni di riga (\n, \r\n, \r) e suddivide di conseguenza, rendendolo più robusto di split('\n') per elaborare testo proveniente da sorgenti diverse.
6.2) Formattare stringhe con le f-string
Creare output formattato è uno dei compiti più comuni in programmazione. Devi combinare testo con valori di variabili, allineare colonne, formattare numeri e creare output leggibile per gli utenti. Le f-string di Python (formatted string literals) forniscono il modo più moderno, leggibile e potente per farlo.
6.2.1) Sintassi di base delle f-string
Una f-string è una stringa letterale prefissata con f o F che può contenere espressioni tra parentesi graffe {}. Python valuta queste espressioni e converte i loro risultati in stringhe:
# fstring_basic.py
name = "Alice"
age = 30
greeting = f"Hello, {name}! You are {age} years old."
print(greeting) # Output: Hello, Alice! You are 30 years old.Le espressioni all'interno di {} possono essere qualsiasi espressione Python valida:
# fstring_expressions.py
x = 10
y = 20
result = f"The sum of {x} and {y} is {x + y}"
print(result) # Output: The sum of 10 and 20 is 30
price = 19.99
quantity = 3
total = f"Total cost: ${price * quantity}"
print(total) # Output: Total cost: $59.976.2.2) Perché le f-string sono migliori degli approcci più vecchi
Prima delle f-string (introdotte in Python 3.6), i programmatori usavano la concatenazione di stringhe o il metodo format(). Confrontiamoli:
Concatenazione di stringhe (il modo vecchio e soggetto a errori):
# concatenation_example.py
name = "Bob"
age = 25
# Richiede la conversione dei numeri in stringhe e molti operatori +
message = "Hello, " + name + "! You are " + str(age) + " years old."
print(message) # Output: Hello, Bob! You are 25 years old.Questo approccio è prolisso, soggetto a errori (dimenticare str() causa errori) e difficile da leggere quando ci sono molte variabili.
f-string (il modo moderno e pulito):
# fstring_clean.py
name = "Bob"
age = 25
message = f"Hello, {name}! You are {age} years old."
print(message) # Output: Hello, Bob! You are 25 years old.Le f-string convertono automaticamente i valori in stringhe, sono più leggibili e sono in realtà più veloci rispetto ad altri approcci.
6.2.3) Espressioni e chiamate a metodi nelle f-string
Puoi includere espressioni complesse, chiamate a metodi e persino chiamate a funzioni all'interno delle f-string:
# fstring_methods.py
name = "alice"
print(f"Capitalized: {name.capitalize()}") # Output: Capitalized: Alice
print(f"Uppercase: {name.upper()}") # Output: Uppercase: ALICE
print(f"Length: {len(name)}") # Output: Length: 5
# Aritmetica e confronti
x = 10
print(f"Is {x} even? {x % 2 == 0}") # Output: Is 10 even? True
# Indicizzazione e slicing
text = "Python"
print(f"First letter: {text[0]}") # Output: First letter: P
print(f"First three: {text[:3]}") # Output: First three: Pyt6.2.4) Formattare numeri nelle f-string
Le f-string supportano specificatori di formato (format specifier) che controllano come vengono visualizzati i valori. La sintassi è {expression:format_spec}:
Controllare le cifre decimali per i float:
# fstring_decimals.py
pi = 3.14159265359
print(f"Default: {pi}") # Output: Default: 3.14159265359
print(f"2 decimals: {pi:.2f}") # Output: 2 decimals: 3.14
print(f"4 decimals: {pi:.4f}") # Output: 4 decimals: 3.1416
print(f"No decimals: {pi:.0f}") # Output: No decimals: 3Lo specificatore di formato .2f significa "formatta come float con 2 cifre decimali". La f sta per "fixed-point notation".
Formattare con separatori delle migliaia:
# fstring_thousands.py
large_number = 1234567890
print(f"No separator: {large_number}") # Output: No separator: 1234567890
print(f"With commas: {large_number:,}") # Output: With commas: 1,234,567,890
print(f"With underscores: {large_number:_}") # Output: With underscores: 1_234_567_890
# In combinazione con le cifre decimali
price = 1234567.89
print(f"Price: ${price:,.2f}") # Output: Price: $1,234,567.89Formattazione in percentuale:
# fstring_percentage.py
ratio = 0.847
print(f"Ratio: {ratio}") # Output: Ratio: 0.847
print(f"Percentage: {ratio:.1%}") # Output: Percentage: 84.7%
print(f"Percentage: {ratio:.2%}") # Output: Percentage: 84.70%Lo specificatore % moltiplica per 100 e aggiunge il simbolo di percentuale.
6.2.5) Esempi pratici con le f-string
Creare report formattati:
# report_example.py
product = "Laptop"
price = 899.99
quantity = 5
tax_rate = 0.08
subtotal = price * quantity
tax = subtotal * tax_rate
total = subtotal + tax
print(f"Product: {product}") # Output: Product: Laptop
print(f"Price: ${price:.2f}") # Output: Price: $899.99
print(f"Quantity: {quantity}") # Output: Quantity: 5
print(f"Subtotal: ${subtotal:.2f}") # Output: Subtotal: $4499.95
print(f"Tax (8%): ${tax:.2f}") # Output: Tax (8%): $360.00
print(f"Total: ${total:.2f}") # Output: Total: $4859.95Creare messaggi user-friendly:
# user_messages.py
username = "Alice"
login_count = 42
last_login = "2024-03-15"
welcome = f"Welcome back, {username}!"
stats = f"You've logged in {login_count} times. Last login: {last_login}"
print(welcome) # Output: Welcome back, Alice!
print(stats) # Output: You've logged in 42 times. Last login: 2024-03-156.2.6) Fare debugging con le f-string
Python 3.8 ha introdotto lo specificatore = per il debugging, che mostra sia l'espressione che il suo valore:
# fstring_debug.py
x = 10
y = 20
z = x + y
print(f"{x=}") # Output: x=10
print(f"{y=}") # Output: y=20
print(f"{z=}") # Output: z=30
print(f"{x + y=}") # Output: x + y=30Questo è estremamente utile per controllare rapidamente i valori delle variabili durante lo sviluppo senza dover digitare due volte il nome della variabile.
6.2.7) Fare l'escape delle parentesi graffe nelle f-string
Se hai bisogno di parentesi graffe letterali in una f-string, raddoppiale:
# fstring_escape.py
value = 42
# Le parentesi singole sono segnaposto per espressioni
print(f"Value: {value}") # Output: Value: 42
# Le parentesi doppie producono parentesi letterali
print(f"Use {{value}} as a placeholder") # Output: Use {value} as a placeholder
print(f"The value is {value}, shown as {{value}}") # Output: The value is 42, shown as {value}6.3) Formattazione con format() e specificatori di formato
Anche se le f-string sono l'approccio moderno preferito, il metodo format() è ancora ampiamente usato e offre alcune capacità che è utile comprendere. È anche la base su cui si costruiscono le f-string, quindi capire format() ti aiuta a comprendere meglio le f-string.
6.3.1) Sintassi di base di format()
Il metodo format() usa parentesi graffe {} come segnaposto in una stringa, e i valori da inserire vengono passati come argomenti:
# format_basic.py
template = "Hello, {}! You are {} years old."
message = template.format("Alice", 30)
print(message) # Output: Hello, Alice! You are 30 years old.
# Uso multiplo di format()
greeting = "Hello, {}!".format("Bob")
print(greeting) # Output: Hello, Bob!6.3.2) Argomenti posizionali e keyword
Puoi controllare quale argomento va in quale posizione usando numeri di posizione o nomi:
Argomenti posizionali:
# format_positional.py
# Ordine predefinito (0, 1, 2, ...)
template = "{} + {} = {}"
result = template.format(5, 3, 8)
print(result) # Output: 5 + 3 = 8
# Posizioni esplicite
template = "{0} + {1} = {2}"
result = template.format(5, 3, 8)
print(result) # Output: 5 + 3 = 8
# Riordinare e riutilizzare
template = "{2} = {0} + {1}"
result = template.format(5, 3, 8)
print(result) # Output: 8 = 5 + 3
# Riutilizzare lo stesso valore
template = "{0} times {0} equals {1}"
result = template.format(7, 49)
print(result) # Output: 7 times 7 equals 49Argomenti keyword:
# format_keyword.py
template = "Hello, {name}! You are {age} years old."
message = template.format(name="Alice", age=30)
print(message) # Output: Hello, Alice! You are 30 years old.
# Può essere mescolato con i posizionali (i posizionali devono venire per primi)
template = "{0}, your score is {score} out of {1}"
result = template.format("Alice", 100, score=95)
print(result) # Output: Alice, your score is 95 out of 1006.3.3) Specificatori di formato con format()
Gli specificatori di formato funzionano allo stesso modo in format() e nelle f-string, usando il separatore ::
# format_specifiers.py
pi = 3.14159265359
print("{:.2f}".format(pi)) # Output: 3.14
print("{:.4f}".format(pi)) # Output: 3.1416
# Con nomi
print("{value:.2f}".format(value=pi)) # Output: 3.14
# Valori multipli con formati differenti
template = "{name}'s score is {score:.1f}%"
result = template.format(name="Bob", score=87.654)
print(result) # Output: Bob's score is 87.7%6.3.4) Quando usare format() invece delle f-string
Le f-string sono generalmente preferite, ma format() è utile in situazioni specifiche:
1. Stringhe template definite separatamente dai dati:
# format_templates.py
# Template definito una volta, usato più volte con dati diversi
email_template = "Dear {name},\n\nYour order #{order_id} has shipped.\n\nThank you!"
# Usa il template con clienti diversi
message1 = email_template.format(name="Alice", order_id=12345)
message2 = email_template.format(name="Bob", order_id=12346)
print(message1)
# Output:
# Dear Alice,
#
# Your order #12345 has shipped.
#
# Thank you!
print(message2)
# Output:
# Dear Bob,
#
# Your order #12346 has shipped.
#
# Thank you!2. Quando il template proviene da sorgenti esterne:
# format_external.py
# Il template potrebbe provenire da un file di configurazione o da un database
# (Impareremo a leggere i file nel Capitolo 24)
user_template = input("Enter message template: ")
# L'utente inserisce: "Hello, {name}! Welcome to {place}."
message = user_template.format(name="Charlie", place="Python")
print(message) # Output: Hello, Charlie! Welcome to Python.Con le f-string, il template deve trovarsi nel tuo codice perché le espressioni vengono valutate immediatamente. Con format(), il template può essere una stringa normale proveniente da qualsiasi sorgente.
6.3.5) Accedere ad attributi di oggetti e chiavi di dizionari
Il metodo format() può accedere direttamente ad attributi e chiavi di dizionari:
# format_access.py
# Accesso a dizionario
person = {"name": "Alice", "age": 30, "city": "Boston"}
message = "Name: {0[name]}, Age: {0[age]}, City: {0[city]}".format(person)
print(message) # Output: Name: Alice, Age: 30, City: Boston
# Con argomento keyword
message = "{p[name]} is {p[age]} years old".format(p=person)
print(message) # Output: Alice is 30 years oldNota: Impareremo gli attributi degli oggetti nel Capitolo 30, ma questo esempio mostra che format() può accedere a strutture dati annidate.
6.4) Allineare e arrotondare numeri nell'output formattato
Un output dall'aspetto professionale richiede spesso un attento allineamento e una corretta formattazione dei numeri. Sia le f-string che format() forniscono strumenti potenti per creare tabelle, report e visualizzazioni ben formattate.
6.4.1) Allineamento del testo
Puoi controllare la larghezza e l'allineamento dei valori usando i specificatori di formato:
# alignment_basic.py
# Sintassi: {value:width}
# Il default è allineato a sinistra per le stringhe, a destra per i numeri
name = "Alice"
print(f"|{name}|") # Output: |Alice|
print(f"|{name:10}|") # Output: |Alice | (allineato a sinistra, larghezza 10)
print(f"|{name:>10}|") # Output: | Alice| (allineato a destra)
print(f"|{name:^10}|") # Output: | Alice | (centrato)Gli specificatori di allineamento sono:
<: allineamento a sinistra (default per le stringhe)>: allineamento a destra (default per i numeri)^: allineamento centrato
Allineamento con i numeri:
# alignment_numbers.py
value = 42
print(f"|{value}|") # Output: |42|
print(f"|{value:5}|") # Output: | 42| (allineato a destra per default)
print(f"|{value:<5}|") # Output: |42 | (allineato a sinistra)
print(f"|{value:^5}|") # Output: | 42 | (centrato)6.4.2) Caratteri di riempimento personalizzati
Puoi specificare un carattere per riempire lo spazio vuoto:
# alignment_fill.py
name = "Bob"
print(f"|{name:*<10}|") # Output: |Bob*******|
print(f"|{name:*>10}|") # Output: |*******Bob|
print(f"|{name:*^10}|") # Output: |***Bob****|
# Utile per creare separatori visivi
print(f"{name:=^20}") # Output: ========Bob=========La sintassi è {value:fill_char align width}.
6.4.3) Combinare allineamento e formattazione dei numeri
Puoi combinare larghezza, allineamento e formattazione dei numeri:
# alignment_combined.py
price = 19.99
quantity = 5
total = price * quantity
# Allineamento a destra con larghezza 10, 2 cifre decimali
print(f"Price: ${price:>10.2f}") # Output: Price: $ 19.99
print(f"Quantity: {quantity:>10}") # Output: Quantity: 5
print(f"Total: ${total:>10.2f}") # Output: Total: $ 99.95
# Con carattere di riempimento per effetto visivo
print(f"Total: ${total:>10.2f}".replace(' ', '.')) # Output: Total:....$.....99.95In questo capitolo hai imparato tecniche essenziali di manipolazione delle stringhe che userai praticamente in ogni programma Python: suddividere e unire stringhe per l'elaborazione del testo, creare output formattato con f-string e specificatori di formato, allineare e formattare numeri per visualizzazioni professionali.
Queste abilità costituiscono le fondamenta per lavorare con dati testuali in Python. Ora puoi elaborare input dell'utente, creare report formattati, gestire dati da file (che tratteremo nel Capitolo 24). Continuando a imparare Python, userai costantemente queste tecniche di gestione delle stringhe, quindi esercitale finché non ti verranno naturali.