Vai al contenuto

Capitolo 3.5 · Stringhe

Cosa impari

  • che cos'è una stringa e come si calcola la sua lunghezza con len;
  • a unire e ripetere stringhe con + e *, e a convertire numeri in stringhe e viceversa;
  • a leggere singoli caratteri e porzioni di stringa con gli indici e lo slicing;
  • a usare ord, chr, le sequenze di escape e i metodi lower, upper, replace;
  • a formattare l'output con l'operatore % e con le f-string.

Molti programmi elaborano testi, non soltanto numeri: nomi, codici, indirizzi, numeri di telefono. In questo capitolo impari a lavorare con le stringhe: prenderne un pezzo, unirle, trasformarle e mostrarle in modo ordinato.

Il tipo stringa

Una stringa è una sequenza di caratteri: lettere, cifre, spazi, simboli. Ogni carattere appartiene allo standard Unicode, che assegna un numero a ogni carattere esistente. La stringa è un tipo strutturato (o non scalare): è composta da unità più piccole, i caratteri.

Una stringa si scrive tra doppi apici ("hello") oppure tra apici singoli ('hello'). Le due forme sono equivalenti.

Il numero di caratteri di una stringa si chiama lunghezza e si calcola con la funzione len. Una stringa senza caratteri, scritta "" oppure '', si chiama stringa vuota e ha lunghezza 0.

ciao = "hello"
ciao2 = 'hello world'
print(len(ciao))     # 5
print(len(ciao2))    # 11: anche lo spazio è un carattere
vuota = ""
print(len(vuota))    # 0

Concatenazione e ripetizione

L'operatore + tra due stringhe le concatena: il risultato contiene tutti i caratteri della prima, seguiti da tutti quelli della seconda. Python non aggiunge spazi da solo: se ti serve uno spazio, devi concatenare la stringa " ".

nome = "Mario"
cognome = "Rossi"
persona = nome + cognome
print(persona)           # MarioRossi
persona = nome + " " + cognome
print(persona)           # Mario Rossi

L'operatore * tra una stringa e un numero intero ripete la stringa. Il numero può stare a sinistra o a destra del *. È comodo, ad esempio, per disegnare una linea di 50 trattini senza scriverli tutti a mano.

trattini = "-" * 50
print(trattini)
messaggio = "Echo..."
print(messaggio * 5)
print(3 * "ab")

Lo stesso operatore, compiti diversi

+ e * fanno cose diverse a seconda dei valori: tra due numeri + somma, tra due stringhe concatena. Questo si chiama operator overloading (sovraccarico degli operatori). Però stringa + numero non è permesso: Python segnala un errore.

Conversioni tra numeri e stringhe

Non puoi concatenare una stringa con un numero. Prima devi convertire il numero in stringa con la funzione str. Questa conversione si chiama type casting.

nome = "Mario"
codice = 1729
# agente = nome + codice      # TypeError: non si unisce una stringa con un int
valore = str(codice)
agente = nome + valore
print(agente)                 # Mario1729

Al contrario, se una stringa contiene un numero scritto correttamente, la puoi trasformare in un valore numerico con int o float. Ti serve soprattutto con input, che restituisce sempre una stringa.

val = int("1729")
price = float("17.29")
print(val + 1)        # 1730: ora è un numero, si può sommare
print(price * 2)      # 34.58

Se la stringa non rappresenta un numero valido, il programma si ferma con un ValueError. L'esempio che segue mostra apposta questo errore.

val = int("ciao")     # ValueError: "ciao" non è un numero
print(val)

Accesso ai caratteri: gli indici

Ogni carattere di una stringa ha una posizione, detta indice. Il primo carattere ha indice 0, il secondo 1, e così via.

Carattere H a r r y
Indice 0 1 2 3 4

Per leggere un carattere scrivi l'indice tra parentesi quadre: nome[0]. L'ultimo indice è sempre len(nome) - 1, perché si parte da 0.

nome = "Harry"
primo = nome[0]
ultimo = nome[4]
print(primo, ultimo)          # H y
pos = len(nome) - 1           # 5 - 1 = 4
print(nome[pos])              # y

Se usi un indice fuori dalle posizioni valide, durante l'esecuzione ottieni un IndexError (index out of range, indice fuori dall'intervallo). L'esempio che segue mostra apposta questo errore: "Harry" ha 5 caratteri, quindi gli indici vanno da 0 a 4.

stringa = "Harry"
carattere = stringa[5]        # IndexError: l'indice 5 non esiste

Indici negativi

Gli indici possono anche essere negativi: si conta da destra. L'ultimo carattere ha indice -1, il penultimo -2 e così via fino al primo.

Carattere H a r r y
Indice positivo 0 1 2 3 4
Indice negativo -5 -4 -3 -2 -1
stringa = "Harry"
print(stringa[0], stringa[-5])   # H H: stesso carattere
print(stringa[-1])               # y: l'ultimo, senza usare len

Le stringhe sono immutabili

In Python le stringhe sono immutabili: dopo averle create non puoi cambiare i loro caratteri. Se ci provi ottieni un TypeError. L'esempio che segue mostra apposta questo errore.

nome = "Bob"
print(nome[0])
nome[0] = 'G'        # TypeError: 'str' object does not support item assignment

Puoi però assegnare alla variabile una stringa nuova. La stringa vecchia non cambia: la variabile ora fa riferimento a un'altra stringa.

nome = "Bob"
print(nome[0])       # B
nome = "Gerard"
print(nome[0])       # G

Slicing: prendere una porzione di stringa

Lo slicing estrae una parte di una stringa, detta slice (fetta). La sintassi è stringa[start:end]:

  • start è l'indice del primo carattere, incluso;
  • end è l'indice dove fermarsi, escluso.

Nella stringa "Corso di Elementi di Informatica" la parola Elementi va dall'indice 9 (la E) all'indice 16 (la i). Quindi si scrive [9:17]: il 17 è escluso.

stringa = "Corso di Elementi di Informatica"
print(stringa[9:17])    # 'Elementi'
print(stringa[9:16])    # 'Element': manca l'ultima lettera
print(stringa[9:18])    # 'Elementi ': c'è anche lo spazio

Entrambi gli indici sono facoltativi:

  • se ometti start, si parte dal primo carattere (indice 0);
  • se ometti end, si arriva fino all'ultimo carattere;
  • stringa[:] è una copia dell'intera stringa.
stringa = "Elementi di Informatica"
print(stringa[:8])      # 'Elementi'
print(stringa[9:])      # 'di Informatica'
print(stringa[:])       # 'Elementi di Informatica'
print(stringa[-3:])     # 'ica': gli ultimi tre caratteri

Slicing con passo

Puoi aggiungere un terzo numero, il passo (step): stringa[start:end:step]. Se non lo scrivi, il passo vale 1 e si prendono tutti i caratteri. Con passo 2 si prende un carattere sì e uno no.

stringa = "Elementi di Informatica"
print(len(stringa))       # 23
print(stringa[0:22:2])    # 'Eeet iIfrai'

Il passo può essere anche negativo: allora ci si sposta all'indietro e i caratteri escono in ordine inverso. In questo caso start deve essere maggiore di end. Con [::-1] ottieni l'intera stringa al contrario.

stringa = "Elementi di Informatica"
print(stringa[22:5:-1])   # 'acitamrofnI id it'
print(stringa[::-1])      # tutta la stringa al contrario

Come ricordare dove finisce lo slice

In stringa[start:end] il numero di caratteri estratti è end - start. Ad esempio stringa[9:17] restituisce 17 − 9 = 8 caratteri, proprio come le 8 lettere di Elementi.

I codici dei caratteri: ord e chr

Il computer memorizza ogni carattere come un numero: il suo codice Unicode, scritto in binario. Python ha due funzioni per passare dal carattere al numero e viceversa:

  • ord(carattere) restituisce il codice Unicode del carattere;
  • chr(codice) restituisce il carattere che ha quel codice.

Le lettere maiuscole e minuscole hanno codici diversi: tra A (65) e a (97) la distanza è 32.

print('a:', ord('a'), 'A:', ord('A'))
print('Distanza tra maiuscola e minuscola:', ord('a') - ord('A'))
print('Lettera corrispondente a code 68: ', chr(68))
print('Lettera corrispondente a code 75: ', chr(75))
print(chr(ord('a') - 32))     # A
print(chr(ord('A') + 32))     # a

Sequenze di escape

Come scrivi una stringa che contiene le virgolette? Metti un backslash (\) prima delle virgolette. Il backslash non compare nella stringa: dice solo a Python che le virgolette che seguono fanno parte del testo. Un backslash seguito da un carattere si chiama sequenza di escape.

Sequenza Significato
\" virgolette doppie dentro la stringa
\' apice singolo dentro la stringa
\n a capo (newline): inizia una nuova riga
\t tabulazione orizzontale
\\ il carattere backslash
print("Lui disse: \"Ciao!\"")
print('L\'esame è domani')
print("Prima riga\nSeconda riga")
print("Nome\tVoto")
print("Anna\t28")

Funzioni e metodi

In Python ogni valore è un oggetto. Un metodo è una funzione che appartiene a un certo tipo di oggetto: ad esempio, tutte le stringhe hanno gli stessi metodi. Un metodo si chiama con la notazione del punto: oggetto.metodo().

Funzioni Metodi
sono generali, accettano argomenti di tipi diversi sono specifici di un tipo (stringhe, interi, ...)
si chiamano così: len(s) si chiamano con il punto: s.upper()
il risultato si salva in una variabile: n = len(s) il risultato si salva in una variabile: m = s.upper()

Tre metodi delle stringhe molto utili:

Metodo Restituisce
s.lower() la versione di s tutta in minuscolo
s.upper() la versione di s tutta in maiuscolo
s.replace(old, new) una nuova stringa in cui ogni old è sostituito da new

Ricorda che le stringhe sono immutabili: i metodi non cambiano s, ma restituiscono una stringa nuova. Se vuoi conservarla, devi assegnarla a una variabile.

s = "Elementi di Informatica"
print(s.lower())
print(s.upper())
print(s.replace("i", "1"))
s.upper()
print(s)                  # s non è cambiata!
s = s.upper()
print(s)                  # ora sì: abbiamo assegnato il risultato

I metodi lower e upper sono comodi nei confronti. Nel capitolo 3 hai visto che 'Prospero' è diverso da 'prospero'. Se converti la risposta in minuscolo, l'utente può scrivere come vuole.

risposta = input('Vuoi continuare? (si/no) ')
if risposta.lower() == 'si':
    print('Continuiamo!')
else:
    print('Ci fermiamo qui.')

Prova a rispondere si, SI e Si: il programma continua in tutti e tre i casi.

Leggere dati con input

La funzione input mostra un messaggio (il prompt) e aspetta che l'utente scriva qualcosa e prema Invio. Restituisce sempre una stringa. Per leggere un numero, prima leggi la stringa e poi la converti con int o float.

nome = input("Inserisci il tuo nome: ")
eta_stringa = input("Inserisci la tua età: ")
eta = int(eta_stringa)
print(nome, "tra un anno avrai", eta + 1, "anni")

Qui eta_stringa è una variabile temporanea: serve solo a contenere il testo prima della conversione. Di solito si scrive tutto in una riga: eta = int(input("Inserisci la tua età: ")).

Output formattato

Spesso vuoi inserire dei numeri in un testo e mostrarli in modo ordinato, ad esempio sempre con due decimali. Python offre diverse soluzioni: la concatenazione, l'operatore %, le f-string e il metodo .format(). Qui vediamo l'operatore % e le f-string.

L'operatore di formattazione %

Tra due numeri interi % calcola il resto della divisione. Se invece a sinistra c'è una stringa, % diventa l'operatore di formato. Nella stringa scrivi degli specificatori di formato (come %d): ognuno viene sostituito, nell'ordine, dai valori scritti dopo il %.

Specificatore Significato
%s stringa (o altro valore mostrato come testo, ad esempio un numero)
%d numero intero
%f numero reale (float), di default con 6 decimali
%.2f numero reale con 2 cifre dopo il punto
%10.2f numero reale con 2 decimali in un campo largo 10 caratteri, allineato a destra
%-10s stringa in un campo largo 10, allineata a sinistra (il -)
%x numero intero in base 16 (esadecimale)
prezzo = 0.7523
print("Prezzo al litro intero: %d" % (prezzo))
print("Prezzo al litro reale: %f" % (prezzo))
print("Prezzo al litro reale con due cifre decimali: %.2f" % (prezzo))
print("Prezzo al litro reale con due cifre decimali su 10 spazi: %10.2f" % (prezzo))
valore = 10
print("Valore hex: %x" % (valore))
titolo = "Quantità"
print("%10s:%10d" % (titolo, 24))
print("%-10s:%10d" % (titolo, 24))

Nota che %d con 0.7523 mostra 0: la parte decimale viene troncata.

Le f-string

Una f-string è una stringa preceduta dalla lettera f (o F). Dentro puoi scrivere, tra parentesi graffe {}, una variabile o un'espressione: Python la calcola al momento dell'esecuzione e mette il risultato al suo posto. Se aggiungi = dopo il nome, viene mostrato anche il nome della variabile: è utile per controllare i valori mentre scrivi il programma.

result = 5
a = 7
b = 8
print(f"the result is {result}")
print(f"the sum is {a+b}")
print(f"the result is {result=}")

Per decidere come mostrare un valore, scrivi uno specificatore di formato dopo i due punti: {valore:specifica}.

Specificatore Significato
<, >, ^ allinea a sinistra, a destra o al centro nello spazio disponibile
s formato stringa (è quello di default, si può omettere)
+ mostra sempre il segno, anche per i numeri positivi
b, d, o, x intero in binario, decimale, ottale, esadecimale
f, e numero reale in notazione fissa o esponenziale

Per i numeri reali il formato più usato è {valore:ampiezza.precisionef}:

  • ampiezza: numero minimo di caratteri stampati (cifre, segno e punto compresi). Lo spazio in più è riempito con spazi e il numero è allineato a destra;
  • precisione: numero di cifre dopo il punto decimale.

Nell'esempio, import math permette di usare il valore di π già pronto in Python, math.pi.

valore = 20
print(f'Binario: {valore:b}')
print(f'Esadecimale: {valore:x}')
print(f'Ottale: {valore:o}')
import math
pi_greco = math.pi
print(f'{pi_greco:10.3f}')
print(f'{pi_greco:8.6f}')
nome = 'Anna'
print(f'[{nome:<10}]')
print(f'[{nome:>10}]')
print(f'[{nome:^10}]')

% o f-string?

Operatore % f-string
"la tua età è %d" % (eta) f'la tua età è {eta}'
nella stringa scrivi un segnaposto con il tipo (%d, %s, ...) la stringa è preceduta dalla lettera f
i valori si scrivono dopo, in ordine: % (val1, val2) tra {} scrivi direttamente la variabile o un'espressione

Le f-string sono più facili da leggere, perché vedi subito quale variabile va in quale punto.

Esempio completo: il distributore automatico

Un cliente sceglie un articolo e inserisce una banconota. Il distributore dà il resto con monete da un dollaro e da un quarto di dollaro (25 centesimi). I prezzi sono multipli di 25 centesimi. Quante monete di ogni tipo deve dare?

  • Input: il prezzo dell'articolo (in centesimi) e il valore della banconota (in dollari).
  • Output: il numero di monete da un dollaro e da un quarto.

Risoluzione a mano. L'articolo costa 2.25 $ e il cliente inserisce 5 $. Conviene ragionare in centesimi: il resto è 500 − 225 = 275 centesimi.

  • 275 // 100 = 2 → 2 monete da un dollaro;
  • 275 % 100 = 75 centesimi avanzano;
  • 75 // 25 = 3 → 3 monete da un quarto.
CENTESIMI_PER_DOLLARO = 100
CENTESIMI_PER_QUARTO = 25
prezzo_articolo = int(input("Inserire prezzo articolo in centesimi: "))
banconota = int(input("Inserire banconota (solo valore numerico): "))
resto_dovuto = CENTESIMI_PER_DOLLARO * banconota - prezzo_articolo
resto_dollari = resto_dovuto // CENTESIMI_PER_DOLLARO
pf_resto_dollari = resto_dovuto % CENTESIMI_PER_DOLLARO
resto_quarti = pf_resto_dollari // CENTESIMI_PER_QUARTO
print(f'Monete da un dollaro: {resto_dollari:d} \nMonete da un quarto: {resto_quarti:d}')

Prova con 225 e 5 (2 dollari e 3 quarti), poi con 175 e 10 (8 dollari e 1 quarto). Nota il \n dentro la f-string: manda a capo il secondo messaggio.

Errori tipici

Errore Cosa succede Come si corregge
'Hai ' + 20 + ' anni' TypeError: non si concatena una stringa con un numero converti con str(20) oppure usa una f-string
int('12,5') o int('ciao') ValueError: la stringa non è un numero intero valido controlla il testo; per i decimali usa float('12.5')
nome[5] con nome = "Harry" IndexError: gli indici vanno da 0 a 4 l'ultimo indice è len(nome) - 1 oppure -1
nome[0] = 'G' TypeError: le stringhe sono immutabili crea una stringa nuova: nome = 'G' + nome[1:]
s.upper() senza assegnare nessun errore, ma s resta uguale s = s.upper()
s[9:16] per prendere 8 caratteri nessun errore, ma manca l'ultimo carattere end è escluso: scrivi s[9:17]
print('Totale: {totale}') stampa proprio {totale}, non il valore manca la f: f'Totale: {totale}'

Esercizi

Esercizio 1 · Inizio e fine. Memorizza una stringa in una variabile. Mostra i primi tre caratteri, poi tre punti, poi gli ultimi tre caratteri. Con "Mississippi" il programma deve mostrare Mis...ppi. Prova poi con una parola di 4 lettere e con una di 2: che cosa succede?

# Scrivi qui la tua soluzione
Soluzione
parola = "Mississippi"
risultato = parola[:3] + "..." + parola[-3:]
print(risultato)

Con "casa" ottieni cas...asa: le due parti si sovrappongono, perché la parola ha meno di 6 caratteri. Con "no" ottieni no...no: lo slicing non dà errore se gli indici escono dalla stringa, prende solo i caratteri che ci sono.

Esercizio 2 · Numero di telefono. Chiedi un numero di telefono di dieci cifre, come 4155551212. Mostralo nello stile statunitense: (415) 555-1212.

# Scrivi qui la tua soluzione
Soluzione
numero = input("Numero di telefono (10 cifre): ")
prefisso = numero[:3]
centrale = numero[3:6]
finale = numero[6:]
print("(" + prefisso + ") " + centrale + "-" + finale)

Con una f-string l'ultima riga diventa print(f"({prefisso}) {centrale}-{finale}").

Esercizio 3 · Palindromo. Una frase è palindroma se letta al contrario resta uguale, senza contare spazi e maiuscole: ad esempio I topi non avevano nipoti. Chiedi una frase e scrivi se è palindroma oppure no. Suggerimento: usa lower, replace e lo slicing con passo -1.

# Scrivi qui la tua soluzione
Soluzione
frase = input("Scrivi una frase: ")
pulita = frase.lower()
pulita = pulita.replace(" ", "")
if pulita == pulita[::-1]:
    print(f'"{frase}" è palindroma')
else:
    print(f'"{frase}" non è palindroma')

Prova con I topi non avevano nipoti (palindroma) e con Elementi di Informatica (non palindroma).

Verifica