Vai al contenuto

Capitolo 6 · File ed eccezioni

Cosa impari

  • perché si salvano i dati in un file e quali sono i tre passaggi: aprire, elaborare, chiudere;
  • ad aprire un file con open nelle modalità 'r', 'w' e 'a';
  • a scrivere con write e a leggere con read e readline, togliendo il \n con rstrip;
  • a salvare e rileggere numeri con str, int e float;
  • a leggere un file di lunghezza sconosciuta con un ciclo while o for;
  • a organizzare i dati in record e campi;
  • che cos'è un'eccezione, come evitarla e come gestirla con try/except.

I programmi visti finora perdono i dati quando terminano. Le variabili stanno nella memoria RAM, che è volatile: alla fine dell'esecuzione tutto sparisce e la volta dopo l'utente deve inserire di nuovo i dati. Per conservarli tra un'esecuzione e l'altra si salvano in un file, registrato sul disco: i dati diventano persistenti. È quello che fanno quasi tutti i programmi che usi ogni giorno: un editor di testi salva i documenti, un foglio di calcolo i numeri e le formule, un gioco i punteggi.

File di input e di output

  • Scrivere su un file significa copiare dei dati da una variabile (in RAM) nel file. Il file in cui scrivi si chiama file di output.
  • Leggere da un file significa copiare dei dati dal file nella RAM, dove una variabile vi fa riferimento. Il file da cui leggi si chiama file di input.

Per usare un file in un programma servono sempre tre passaggi:

  1. Aprire il file. Si crea un collegamento tra il file e il programma. Aprire un file di output lo crea sul disco; aprire un file di input permette di leggerlo.
  2. Elaborare il file. È l'operazione vera e propria di scrittura o di lettura.
  3. Chiudere il file. Quando hai finito, scolleghi il file dal programma.

Tipi di file e modi di accesso

Tipo Cosa contiene
File di testo dati (anche numeri) scritti come caratteri, con codifiche come ASCII o Unicode. Li apri con un editor come il Blocco note.
File binario dati non convertiti in testo. Di solito li legge solo il programma che li ha creati: un editor di testi non li mostra.

Ci sono anche due modi di accedere ai dati:

  • accesso sequenziale: si legge dall'inizio alla fine, senza saltare a un punto preciso (come una vecchia videocassetta);
  • accesso diretto (o casuale): si legge un dato qualsiasi, in qualunque ordine (come un lettore MP3).

In queste pagine usiamo file di testo ad accesso sequenziale.

Ogni file ha un nome e, di solito, un'estensione: i caratteri dopo il punto, che indicano il tipo di dati. Ad esempio vendite.txt è un file di testo, foto.jpg un'immagine, programma.py un programma Python.

Aprire un file: open

Per lavorare con un file, il programma crea in memoria un oggetto file, cioè un oggetto collegato a un file specifico del disco. Una variabile fa riferimento all'oggetto file e si usa per tutte le operazioni. L'oggetto si crea con la funzione open:

variabile_file = open(nome_file, modalità)

La modalità è una stringa che dice come vuoi usare il file:

Modalità Significato Se il file non esiste
'r' sola lettura (read): non puoi modificare il contenuto errore FileNotFoundError
'w' scrittura (write): se il file esiste, il contenuto vecchio viene cancellato lo crea
'a' aggiunta (append): i dati nuovi vanno in fondo, il contenuto vecchio resta lo crea
customer_file = open('customers.txt', 'r')   # per leggere
sales_file = open('sales.txt', 'w')          # per scrivere da zero
log_file = open('log.txt', 'a')              # per aggiungere in fondo

Attenzione a 'w'

Aprire in 'w' un file che esiste già lo svuota subito, anche se poi non scrivi niente. Se vuoi conservare i dati vecchi usa 'a'.

Il percorso del file

Se scrivi solo il nome del file, Python lo cerca nella stessa cartella del programma. Se il file si trova altrove devi scrivere anche il percorso. In Windows il percorso contiene dei backslash (\): metti una r davanti alla stringa, così Python non li scambia per sequenze di escape come \n o \t.

test_file = open(r'C:\Utenti\studente\dati\test.txt', 'w')

Scrivere su un file: write

Un metodo è una funzione che appartiene a un oggetto e lavora su quell'oggetto (l'hai già visto con le stringhe: s.upper()). Gli oggetti file hanno il metodo write, che scrive una stringa nel file:

variabile_file.write(stringa)

Chiudere un file: close

Quando hai finito, chiudi il file con il metodo close:

variabile_file.close()

Perché è importante? Per essere più veloce, il sistema non scrive subito sul disco: mette i dati in un buffer, una zona temporanea in memoria, e li copia sul disco solo quando il buffer è pieno. Scrivere in memoria è molto più rapido che scrivere su disco. La chiusura forza la scrittura dei dati rimasti nel buffer: se dimentichi close, in alcuni sistemi puoi perderli.

Esempio completo

Il programma apre philosophers.txt in scrittura, scrive tre nomi e chiude il file. Ogni nome finisce con \n, il carattere di nuova riga: così ogni nome sta su una riga diversa.

# Apre il file philosophers.txt in scrittura
outfile = open('philosophers.txt', 'w')

# Scrive i nomi di tre filosofi nel file
outfile.write('John Locke\n')
outfile.write('David Hume\n')
outfile.write('Edmund Burke\n')

# Chiude il file
outfile.close()
print('Ho scritto il file philosophers.txt')

Dopo l'esecuzione il file contiene tre righe:

John Locke
David Hume
Edmund Burke

Dove finisce il file negli esempi del sito

Ogni esempio eseguibile di questa pagina parte da una cartella vuota, e i file creati spariscono alla fine. Per questo gli esempi che leggono un file prima lo creano nello stesso programma, poi lo rileggono. Nei laboratori a più file (le pagine Esercitazioni, Esame e il Foglio libero) è diverso: i file di dati compaiono come schede in alto, proprio come i file in una cartella di VSCodium. Se il programma scrive in un file, la scheda si aggiorna e vedi subito il contenuto nuovo.

Leggere da un file: read

Per leggere apri il file in modalità 'r'. Il metodo read restituisce tutto il contenuto del file come un'unica stringa, che puoi stampare con print. Il programma prima crea il file, poi lo legge.

# 1. Crea il file (come nell'esempio precedente)
outfile = open('philosophers.txt', 'w')
outfile.write('John Locke\n')
outfile.write('David Hume\n')
outfile.write('Edmund Burke\n')
outfile.close()

# 2. Legge tutto il contenuto in una volta
infile = open('philosophers.txt', 'r')
file_contents = infile.read()
infile.close()
print(file_contents)

Leggere una riga alla volta: readline

Spesso conviene leggere ed elaborare un elemento alla volta. Pensa a un file con gli incassi di ogni giorno: per calcolare il totale vuoi leggere un incasso, sommarlo a un accumulatore, poi passare al successivo.

Il metodo readline legge una riga del file. Una riga è una sequenza di caratteri che finisce con \n. readline restituisce la riga come stringa, compreso il \n finale.

# Crea il file
outfile = open('philosophers.txt', 'w')
outfile.write('John Locke\n')
outfile.write('David Hume\n')
outfile.write('Edmund Burke\n')
outfile.close()

# Legge le tre righe una alla volta
infile = open('philosophers.txt', 'r')
line1 = infile.readline()
line2 = infile.readline()
line3 = infile.readline()
infile.close()

print(line1)
print(line2)
print(line3)

Nota le righe vuote tra un nome e l'altro: ogni riga letta contiene già un \n, e print ne aggiunge un altro.

La posizione di lettura

Come fa readline a sapere da quale riga ripartire? Quando apri un file in lettura, Python ricorda una posizione di lettura: il punto da cui leggerà la prossima volta.

  • Appena aperto il file, la posizione è all'inizio.
  • Dopo ogni readline, la posizione avanza alla riga successiva.
  • Se l'ultima riga del file non finisce con \n, readline la restituisce senza \n.
John Locke\nDavid Hume\nEdmund Burke\n
^ dopo open
            ^ dopo la prima readline
                        ^ dopo la seconda readline

E se non sappiamo quante righe ci sono? Usare readline tre volte va bene solo se lo sappiamo. La soluzione è un ciclo: lo vedi più avanti.

Scrivere dati inseriti dall'utente

Di solito non scrivi nel file delle stringhe fisse, ma i valori delle variabili, ad esempio i dati inseriti dall'utente. Per mettere ogni dato su una riga sua, concatena \n al dato prima di scriverlo. Senza \n i nomi finirebbero tutti attaccati sulla stessa riga.

print('Inserisci i nomi di tre amici.')
name1 = input('Amico n. 1: ')
name2 = input('Amico n. 2: ')
name3 = input('Amico n. 3: ')

myfile = open('friends.txt', 'w')
myfile.write(name1 + '\n')
myfile.write(name2 + '\n')
myfile.write(name3 + '\n')
myfile.close()
print('I nomi sono stati scritti in friends.txt.')

# Rilegge il file per controllare
myfile = open('friends.txt', 'r')
print(myfile.read())
myfile.close()

In alternativa puoi scrivere il dato e il \n con due chiamate separate: myfile.write(name1) e poi myfile.write('\n'). Il risultato nel file è lo stesso.

Togliere il \n: rstrip

Nel file il \n è indispensabile, perché separa i dati. Dopo averli letti, però, di solito non ti serve più. Le stringhe hanno il metodo rstrip (r = right, destra; strip = togliere), che elimina i caratteri indicati dalla fine della stringa:

name = name.rstrip('\n')

Come tutti i metodi delle stringhe, rstrip non cambia la stringa originale: restituisce una stringa nuova, che devi assegnare a una variabile.

# Crea il file
outfile = open('philosophers.txt', 'w')
outfile.write('John Locke\n')
outfile.write('David Hume\n')
outfile.write('Edmund Burke\n')
outfile.close()

# Legge le righe e toglie il \n finale
infile = open('philosophers.txt', 'r')
line1 = infile.readline()
line2 = infile.readline()
line3 = infile.readline()
infile.close()

line1 = line1.rstrip('\n')
line2 = line2.rstrip('\n')
line3 = line3.rstrip('\n')

print(line1)
print(line2)
print(line3)

Questa volta non ci sono righe vuote in mezzo.

Scrivere e leggere numeri

write accetta solo stringhe. Per scrivere un numero devi prima convertirlo con la funzione str: se num vale 99, str(num) restituisce la stringa '99'.

Al contrario, quello che leggi da un file di testo è sempre una stringa, anche se contiene un numero. Dopo value = infile.readline() la variabile value vale '22\n', non 22. Per fare dei calcoli devi convertirla con int o float. La conversione funziona anche con il \n finale: int('22\n') dà 22.

Il programma prima chiede tre numeri e li scrive nel file, poi li rilegge e li somma.

# Scrittura: ogni numero diventa una stringa con il suo \n
outfile = open('numbers.txt', 'w')
num1 = int(input('Inserisci un numero: '))
num2 = int(input('Inserisci un altro numero: '))
num3 = int(input('Inserisci un altro numero: '))
outfile.write(str(num1) + '\n')
outfile.write(str(num2) + '\n')
outfile.write(str(num3) + '\n')
outfile.close()
print('Dati scritti in numbers.txt')

# Lettura: ogni riga viene convertita in int
infile = open('numbers.txt', 'r')
num1 = int(infile.readline())
num2 = int(infile.readline())
num3 = int(infile.readline())
infile.close()

total = num1 + num2 + num3
print(f'I numeri sono: {num1}, {num2}, {num3}')
print(f'Il loro totale è: {total}')

Prova a togliere gli int(...) nella parte di lettura: + concatena le stringhe invece di sommare i numeri.

Leggere un file con un ciclo

Di solito un file contiene molti dati e non sai quanti. Serve un ciclo, ma come capisce il programma quando fermarsi? Quando readline prova a leggere oltre la fine del file, restituisce la stringa vuota ''. Lo schema è questo:

  1. apri il file;
  2. leggi la prima riga con readline;
  3. finché il valore letto non è la stringa vuota:
    • elabora la riga appena letta;
    • leggi la riga successiva con readline;
  4. chiudi il file.

La prima lettura prima del ciclo si chiama lettura preliminare: serve a dare un valore alla variabile che il while controlla.

# Crea il file con le vendite di cinque giorni
sales_file = open('sales.txt', 'w')
sales_file.write('1000.0\n')
sales_file.write('2000.0\n')
sales_file.write('3000.0\n')
sales_file.write('4000.0\n')
sales_file.write('5000.0\n')
sales_file.close()

# Legge tutte le righe con un while, senza sapere quante sono
sales_file = open('sales.txt', 'r')
line = sales_file.readline()        # lettura preliminare
total = 0.0
while line != '':
    amount = float(line)
    print(f'{amount:.2f}')
    total += amount
    line = sales_file.readline()    # legge la riga successiva
sales_file.close()
print(f'Totale vendite: {total:.2f}')

Non dimenticare l'ultima readline

Se togli la readline dentro il ciclo, line non cambia mai e il ciclo non termina più.

La versione più semplice: il ciclo for

Python permette di scorrere un file con un ciclo for, come una sequenza: a ogni giro la variabile riceve la riga successiva. Il ciclo si ferma da solo alla fine del file, senza readline e senza controllare ''.

for variabile in oggetto_file:
    istruzioni
# Crea il file con le vendite
sales_file = open('sales.txt', 'w')
sales_file.write('1000.0\n')
sales_file.write('2000.0\n')
sales_file.write('3000.0\n')
sales_file.close()

# Stessa lettura, con il for
sales_file = open('sales.txt', 'r')
total = 0.0
for line in sales_file:
    amount = float(line)
    print(f'{amount:.2f}')
    total += amount
sales_file.close()
print(f'Totale vendite: {total:.2f}')

Esempio guidato: le durate dei video

Un videomaker gira tanti brevi video e poi li monta per creare una pubblicità. Servono due programmi:

  1. il primo chiede la durata in secondi di ogni video e la salva in un file;
  2. il secondo legge il file, mostra le durate e la durata totale.

Nell'esempio i due programmi sono uno dopo l'altro: la prima parte scrive video.txt, la seconda lo legge.

# --- Programma 1: save_running_times ---
num_videos = int(input('Quanti video ci sono nel progetto? '))
video_file = open('video.txt', 'w')
print('Inserisci la durata di ogni video.')
for count in range(1, num_videos + 1):
    run_time = float(input(f'Video n. {count}: '))
    video_file.write(str(run_time) + '\n')
video_file.close()
print('Le durate sono state salvate in video.txt.')

# --- Programma 2: read_running_times ---
video_file = open('video.txt', 'r')
total = 0.0
count = 0
print('Ecco la durata di ogni video:')
for line in video_file:
    run_time = float(line)
    count += 1
    print(f'Video n. {count}: {run_time}')
    total += run_time
video_file.close()
print(f'La durata totale è di {total} secondi.')

Prova con 3 video da 24.5, 12 e 14.2 secondi: il totale è 50.7.

Record e campi

I dati di un file sono spesso organizzati in record. Un record è l'insieme completo dei dati su un elemento; un campo è un singolo dato del record. Ad esempio, in un file di dipendenti ogni record è un dipendente, e i campi sono il nome, la matricola e il reparto.

Il modo più semplice di salvare un record è scrivere un campo per riga, sempre nello stesso ordine:

Mario Rossi        ← record 1, campo nome
1234               ← record 1, campo matricola
Progettazione      ← record 1, campo reparto
Laura Bianchi      ← record 2, campo nome
5678               ← record 2, campo matricola
Amministrazione    ← record 2, campo reparto

Per rileggerlo, a ogni giro del ciclo leggi tutti i campi di un record. Il ciclo while controlla il primo campo: se è la stringa vuota, i record sono finiti.

# Scrittura dei record: un campo per riga
emp_file = open('employees.txt', 'w')
emp_file.write('Mario Rossi' + '\n')
emp_file.write('1234' + '\n')
emp_file.write('Progettazione' + '\n')
emp_file.write('Laura Bianchi' + '\n')
emp_file.write('5678' + '\n')
emp_file.write('Amministrazione' + '\n')
emp_file.close()

# Lettura dei record
emp_file = open('employees.txt', 'r')
name = emp_file.readline()          # primo campo del primo record
while name != '':
    id_num = emp_file.readline()    # gli altri campi dello stesso record
    dept = emp_file.readline()

    name = name.rstrip('\n')
    id_num = id_num.rstrip('\n')
    dept = dept.rstrip('\n')

    print(f'Nome: {name}')
    print(f'Matricola: {id_num}')
    print(f'Reparto: {dept}')
    print()

    name = emp_file.readline()      # primo campo del record successivo
emp_file.close()

Campi sulla stessa riga

Nelle esercitazioni d'esame i campi di un record stanno spesso sulla stessa riga, separati da un carattere come ; (ad esempio 120;10;Conferenza stampa). Per dividerli si usa il metodo split, che userai insieme alle liste nel capitolo 7.

Esempio guidato: il magazzino del caffè

Una piccola torrefazione importa chicchi di caffè e li tosta per creare diverse miscele. Ti chiede dei programmi per gestire il magazzino. Ogni record ha due campi:

  • descrizione: il nome del caffè (stringa);
  • quantità in magazzino: i chilogrammi presenti (numero reale).

Il primo programma aggiunge record al file coffee.txt, quindi lo apre in modalità 'a': i record vecchi restano. Il secondo mostra tutti i record. Usiamo le funzioni del capitolo 5.

def add_coffee_record():
    another = 's'
    coffee_file = open('coffee.txt', 'a')
    while another == 's' or another == 'S':
        descr = input('Descrizione: ')
        qty = float(input('Quantità (in kg): '))
        coffee_file.write(descr + '\n')
        coffee_file.write(str(qty) + '\n')
        another = input('Vuoi aggiungere un altro record? (s/n) ')
    coffee_file.close()
    print('Dati aggiunti a coffee.txt.')

def show_coffee_records():
    coffee_file = open('coffee.txt', 'r')
    descr = coffee_file.readline()
    while descr != '':
        qty = float(coffee_file.readline())
        descr = descr.rstrip('\n')
        print(f'Descrizione: {descr}')
        print(f'Quantità: {qty} kg')
        print()
        descr = coffee_file.readline()
    coffee_file.close()

def main():
    add_coffee_record()
    show_coffee_records()

main()

Prova con Arabica Etiopia, 18.5, poi s, Robusta Brasile, 25, poi n. Qui il file non esiste all'inizio: la modalità 'a', come 'w', lo crea.

Eccezioni

Un'eccezione è un errore che si verifica durante l'esecuzione del programma. Di solito il programma si ferma all'improvviso e Python mostra un traceback: un messaggio che indica le righe che hanno causato il problema, il tipo di eccezione e una breve descrizione.

L'esempio che segue mostra apposta un'eccezione: prova con 10 e 0.

num1 = int(input('Inserisci un numero: '))
num2 = int(input('Inserisci un altro numero: '))
result = num1 / num2
print(f'{num1} diviso {num2} fa {result}')
Traceback (most recent call last):
  File "main.py", line 3, in <module>
    result = num1 / num2
ZeroDivisionError: division by zero

L'ultima riga è la più importante: ZeroDivisionError è il tipo, division by zero la descrizione.

Eccezione Quando succede
ZeroDivisionError dividi per zero
ValueError converti un testo che non è un numero, ad esempio int('ciao')
FileNotFoundError apri in lettura un file che non esiste (o hai sbagliato il nome)
TypeError usi un tipo sbagliato, ad esempio outfile.write(99) invece di outfile.write(str(99))
IndexError usi un indice che non esiste in una stringa

Evitare le eccezioni

Molte eccezioni si evitano scrivendo il codice con attenzione: controllando i dati in ingresso (input validation), verificando che i valori siano del tipo giusto e non nulli. A volte basta un if.

num1 = int(input('Inserisci un numero: '))
num2 = int(input('Inserisci un altro numero: '))
if num2 != 0:
    result = num1 / num2
    print(f'{num1} diviso {num2} fa {result}')
else:
    print('Impossibile dividere per zero.')

Gestire le eccezioni: try / except

Non tutte le eccezioni si prevengono con un if. Se l'utente scrive quaranta invece di 40, int solleva un ValueError e non puoi controllarlo prima in modo semplice. Per questi casi Python ha l'istruzione try/except. Le slide la citano solo per cenni, ma la trovi nel capitolo 6 del libro e la userai spesso nelle esercitazioni d'esame:

try:
    istruzioni che possono sollevare un'eccezione
except NomeEccezione:
    istruzioni da eseguire se l'eccezione si verifica

Python esegue il blocco try. Se tutto va bene, salta l'except. Se invece si verifica un'eccezione del tipo indicato, salta subito al blocco except (le righe del try dopo l'errore non vengono eseguite) e poi il programma continua, invece di fermarsi.

try:
    hours = int(input('Quante ore hai lavorato? '))
    pay_rate = float(input('Paga oraria: '))
    gross_pay = hours * pay_rate
    print(f'Retribuzione lorda: {gross_pay:.2f} euro')
except ValueError:
    print('ERRORE: ore e paga oraria devono essere numeri.')
print('Fine del programma.')

Prova prima con 40 e 12.50, poi scrivi quaranta come numero di ore.

Un caso tipico con i file: il file da leggere non esiste. Il programma prova ad aprire un file che non crea, quindi finisce nell'except.

try:
    infile = open('inesistente.txt', 'r')
    contents = infile.read()
    print(contents)
    infile.close()
except FileNotFoundError:
    print('Il file inesistente.txt non è stato trovato.')

Più except, else e finally

Un try può avere più except, uno per ogni tipo di eccezione: Python esegue quello che corrisponde all'errore. Puoi aggiungere anche:

  • else: eseguito solo se nel try non si è verificata nessuna eccezione;
  • finally: eseguito sempre, con o senza eccezione. Serve per le operazioni finali, come un messaggio di chiusura.

Il programma crea un file di vendite con una riga sbagliata (mille), poi prova a sommarle.

# Crea un file con una riga non numerica
outfile = open('sales.txt', 'w')
outfile.write('1000.0\n')
outfile.write('mille\n')
outfile.write('3000.0\n')
outfile.close()

try:
    infile = open('sales.txt', 'r')
    total = 0.0
    for line in infile:
        amount = float(line)
        total += amount
    infile.close()
except FileNotFoundError:
    print('ERRORE: il file sales.txt non esiste.')
except ValueError:
    print('ERRORE: nel file c\'è un dato non numerico.')
else:
    print(f'Totale vendite: {total:.2f}')
finally:
    print('Elaborazione terminata.')

Correggi 'mille\n' in '2000.0\n' ed esegui di nuovo: questa volta parte l'else con il totale. finally compare in entrambi i casi.

Ripetere l'input finché è valido

Con un ciclo while e un try/except puoi richiedere un dato finché l'utente non lo scrive bene. È uno schema che trovi spesso nelle esercitazioni:

valido = False
while not valido:
    try:
        quantita = int(input('Quantità: '))
        valido = True
    except ValueError:
        print('Devi scrivere un numero intero.')

Errori tipici

Errore Cosa succede Come si corregge
open('dati.txt', 'r') con il nome sbagliato o il file assente FileNotFoundError controlla nome ed estensione (maiuscole comprese) e la cartella
open('dati.txt', 'w') per aggiungere dati il contenuto vecchio viene cancellato per aggiungere in fondo usa 'a'
outfile.write(99) TypeError: write vuole una stringa outfile.write(str(99) + '\n')
outfile.write(name) senza '\n' i dati finiscono tutti sulla stessa riga outfile.write(name + '\n')
total += line con una riga letta TypeError (o concatenazione di stringhe) converti: total += float(line)
print della riga letta senza rstrip compare una riga vuota in più line = line.rstrip('\n')
manca close() in alcuni sistemi i dati restano nel buffer e si perdono chiudi sempre il file alla fine
while line != '': senza readline nel ciclo il ciclo non termina mai leggi la riga successiva alla fine del corpo del ciclo
except ValueError per un file mancante l'eccezione non viene intercettata il file mancante solleva FileNotFoundError

Esercizi

Esercizio 1 · Lista della spesa. Chiedi all'utente tre prodotti e scrivili nel file spesa.txt, uno per riga. Poi rileggi il file con un ciclo for e mostra i prodotti numerati: 1. pane, 2. latte, 3. uova.

# Scrivi qui la tua soluzione
Soluzione
outfile = open('spesa.txt', 'w')
for i in range(3):
    prodotto = input('Prodotto: ')
    outfile.write(prodotto + '\n')
outfile.close()

infile = open('spesa.txt', 'r')
numero = 0
for line in infile:
    numero += 1
    prodotto = line.rstrip('\n')
    print(f'{numero}. {prodotto}')
infile.close()

Esercizio 2 · Media dei voti. Il blocco qui sotto crea il file voti.txt, con un voto per riga. Una riga contiene per errore il testo assente. Leggi il file, salta le righe non numeriche (con un messaggio) e mostra il numero di voti validi, la media con due decimali e quanti voti sono almeno 18.

outfile = open('voti.txt', 'w')
outfile.write('24\n')
outfile.write('17\n')
outfile.write('assente\n')
outfile.write('30\n')
outfile.write('21\n')
outfile.close()

# Scrivi qui la tua soluzione
Soluzione
outfile = open('voti.txt', 'w')
outfile.write('24\n')
outfile.write('17\n')
outfile.write('assente\n')
outfile.write('30\n')
outfile.write('21\n')
outfile.close()

infile = open('voti.txt', 'r')
somma = 0
conta = 0
superati = 0
for line in infile:
    try:
        voto = int(line)
        somma += voto
        conta += 1
        if voto >= 18:
            superati += 1
    except ValueError:
        print('Riga ignorata:', line.rstrip('\n'))
infile.close()

if conta > 0:
    print('Voti validi:', conta)
    print(f'Media: {somma / conta:.2f}')
    print('Esami superati:', superati)
else:
    print('Nessun voto valido nel file.')

Risultato: 4 voti validi, media 23.00, 3 esami superati. L'if conta > 0 evita un ZeroDivisionError se il file non contiene voti validi.

Esercizio 3 · Cerca nel magazzino. Il blocco crea il file coffee.txt con i record del magazzino del caffè (descrizione e quantità, un campo per riga). Scrivi una funzione search_coffee(nome_file, cercato) che legge tutti i record e mostra quelli la cui descrizione è uguale al testo cercato, senza badare a maiuscole e minuscole (suggerimento: confronta le due stringhe dopo averle portate in minuscolo con lower). La funzione restituisce il numero di record trovati. Nel main chiedi il testo da cercare; se non trovi niente scrivi Nessun record trovato. Gestisci con try/except il caso in cui il file non esiste.

coffee_file = open('coffee.txt', 'w')
coffee_file.write('Arabica Etiopia\n18.5\n')
coffee_file.write('Robusta Brasile\n25.0\n')
coffee_file.write('Arabica Colombia\n12.0\n')
coffee_file.close()

# Scrivi qui la tua soluzione
Soluzione
coffee_file = open('coffee.txt', 'w')
coffee_file.write('Arabica Etiopia\n18.5\n')
coffee_file.write('Robusta Brasile\n25.0\n')
coffee_file.write('Arabica Colombia\n12.0\n')
coffee_file.close()

def search_coffee(nome_file, cercato):
    trovati = 0
    infile = open(nome_file, 'r')
    descr = infile.readline()
    while descr != '':
        qty = float(infile.readline())
        descr = descr.rstrip('\n')
        if cercato.lower() == descr.lower():
            print(f'Descrizione: {descr}')
            print(f'Quantità: {qty} kg')
            print()
            trovati += 1
        descr = infile.readline()
    infile.close()
    return trovati

def main():
    cercato = input('Testo da cercare: ')
    try:
        trovati = search_coffee('coffee.txt', cercato)
        if trovati == 0:
            print('Nessun record trovato.')
        else:
            print('Record trovati:', trovati)
    except FileNotFoundError:
        print('Il file coffee.txt non esiste.')

main()

Prova con arabica etiopia (1 record), con ROBUSTA BRASILE (1 record) e con kenya (nessuno). Per provare l'except, cambia il nome del file in 'Coffee.txt' nella chiamata.

Verifica