Corso Python / Leggere e scrivere file di testo con with e codifiche

Per leggere e scrivere un file di testo, specifica la codifica e aprilo con with: il file viene chiuso quando il blocco termina, anche in presenza di un’eccezione. La modalità w cancella subito il contenuto di un file esistente, quindi devi distinguere un risultato ricreabile da dati da conservare.

Scegliere modalità e codifica

Modalità Operazione Comportamento sul percorso
r Lettura Richiede un file esistente
w Scrittura sostitutiva Crea il file oppure tronca il contenuto esistente
a Aggiunta in fondo Crea il file se manca; conserva e prolunga quello esistente
x Creazione esclusiva Fallisce se il percorso esiste già

Una codifica stabilisce come rappresentare i caratteri tramite byte. Se il file è UTF-8, usa encoding="utf-8" sia in scrittura sia in lettura. Non scegliere la codifica in base all’estensione .txt: deve corrispondere a quella con cui i dati sono stati prodotti. Specificarla esplicitamente rende il comportamento chiaro anche tra ambienti Python con impostazioni predefinite diverse.

errors="ignore" può eliminare byte non decodificabili e cambiare i dati senza avvertirti. Per esempio, leggere come UTF-8 un file contenente un carattere accentato codificato in un’altra codifica può fallire; ignorare l’errore può far sparire proprio quel carattere. Individua la codifica reale invece di nascondere il problema. La guida ufficiale alla lettura e scrittura dei file descrive modalità testuale e gestione delle righe.

Scrivere, aggiungere e leggere un esempio completo

Usa una cartella di esercizio senza dati importanti. Salva il codice completo come text_files.py, in UTF-8. Esegui python text_files.py dal terminale nella stessa cartella, con l’interprete del progetto selezionato. Fuori dall’ambiente virtuale su macOS o Ubuntu usa python3 se è il nome del tuo interprete. Non servono dipendenze esterne.

Il file notes-demo.txt è relativo alla cartella di lavoro, come spiegato nella guida a pathlib e Path. La prima apertura lo inizializza a ogni avvio; la seconda aggiunge una riga al contenuto appena scritto.

from pathlib import Path

path = Path("notes-demo.txt")
with path.open("w", encoding="utf-8", newline="\n") as f:
    f.write("coffee:3500\n")
    f.write("lunch:8000\n")
with path.open("a", encoding="utf-8") as f:
    f.write("bus:2500\n")
with path.open("r", encoding="utf-8") as f:
    for line in f:
        print(line.rstrip("\n"))

Il terminale deve mostrare queste tre righe:

coffee:3500
lunch:8000
bus:2500

Le prime due chiamate write non aggiungono automaticamente un fine riga: il carattere \n è scritto esplicitamente nelle stringhe. La modalità a aggiunge bus alla fine; la modalità r percorre poi il file riga per riga. Eseguendo di nuovo la versione originale ottieni sempre tre righe, perché l’apertura iniziale in w elimina quelle della prova precedente.

Nel primo blocco newline="\n" evita la traduzione dei fine riga in scrittura. Nel blocco di aggiunta newline non è specificato: su Windows un \n scritto può diventare \r\n. La lettura testuale predefinita normalizza i comuni fine riga a \n, perciò l’output mostrato resta uguale. Se un formato richiede byte di fine riga uniformi, scegli coerentemente newline in tutti i blocchi di scrittura.

Leggere tutto o procedere per righe

read() restituisce il contenuto rimanente come un’unica stringa. Il ciclo for line in f elabora invece una riga alla volta, evitando di caricare l’intero file quando non serve. Dopo aver consumato il file, un’altra read sullo stesso oggetto restituisce una stringa vuota: non riparte automaticamente dall’inizio. Riapri il file oppure usa intenzionalmente seek(0) prima di rileggerlo.

La riga letta comprende normalmente il carattere finale di nuova riga. Nel programma rstrip("\n") lo rimuove prima che print aggiunga il proprio, evitando righe vuote intermedie. Non sostituirlo senza valutazione con strip(): quest’ultimo rimuove anche spazi e tabulazioni ai bordi, che possono essere parte del dato. Con newline non predefinito controlla anche l’eventuale presenza di \r.

Un file vuoto e un file assente sono situazioni diverse. La stringa vuota ottenuta da read può rappresentare fine file; FileNotFoundError segnala invece che l’apertura del percorso non è riuscita. Sostituire qualsiasi eccezione con una stringa vuota rende indistinguibili queste condizioni e può far procedere il programma con dati incompleti.

Proteggere gli originali e diagnosticare gli errori

With gestisce la chiusura della risorsa, non un annullamento delle modifiche. Se apri in w un documento esistente e un errore interrompe la scrittura, il vecchio contenuto può essere già perduto anche se il file viene chiuso correttamente. Per dati importanti, conserva una copia e valuta una scrittura su un file temporaneo separato, con verifica prima della sostituzione; scegli il comportamento di sostituzione in base al sistema e all’applicazione.

  • FileNotFoundError: controlla il percorso assoluto, la cartella di lavoro e l’esistenza della cartella genitore. Aprire in w crea un file, non tutte le cartelle mancanti.
  • UnicodeDecodeError: verifica come il file è stato codificato e confronta il contenuto con una fonte attendibile; non scartare automaticamente i byte problematici.
  • PermissionError: controlla permessi, eventuali blocchi da altri programmi e che la destinazione non sia una cartella. I dettagli dipendono dal sistema operativo.
  • FileExistsError con x: il rifiuto evita una sostituzione involontaria. Scegli un nuovo nome oppure riesamina deliberatamente la politica di scrittura.

Per osservare la differenza tra sostituzione e aggiunta, lavora su una copia dell’esercizio: cambia soltanto la prima modalità w in a ed esegui due volte. Il file accumula i gruppi di tre righe invece di ripartire da zero; se parti dal file della prova originale, contiene già un gruppo. Ripristina w e avvia nuovamente il programma per tornare alle tre righe previste.