Articolo originale: Python Write to File – Open, Read, Append, and Other File Handling Functions Explained di Estefania Cassingena Navone

Tradotto e adattato da: Dario Di Cillo

Se vuoi imparare come lavorare con i file in Python, allora questo articolo è ciò che ti serve. Lavorare con i file è un'abilità fondamentale che ogni sviluppatore Python dovrebbe avere. Iniziamo!

In quest'articolo, imparerai:

  • Come aprire un file.
  • Come leggere un file.
  • Come creare un file.
  • Come modificare un file.
  • Come chiudere un file.
  • Come aprire dei file per operazioni multiple.
  • Come lavorare con metodi di oggetti file.
  • Come eliminare dei file.
  • Come lavorare con i gestori di contesto e perché sono utili.
  • Come gestire le eccezioni che possono generarsi lavorando con dei file.
  • E altro!

Iniziamo! ✨

🔹 Lavorare con i file: sintassi di base

Una delle funzioni più importanti che avrai bisogno di conoscere per lavorare con i file in Python è open(), una funzione integrata che apre i file e permette al tuo programma di utilizzarli.

Questa è la sintassi di base:

image-48
open() contiene il percorso relativo o assoluto del file e un carattere che indica l'operazione che vuoi svolgere sul file.

💡 Tip: Questi sono gli argomenti usati più di frequente per chiamare questa funzione, ma esistono altri sei argomenti opzionali. Se vuoi saperne di più, puoi leggere quest'articolo della documentazione Python.

Primo parametro: file

Il primo parametro della funzione open() è file, il percorso assoluto o relativo del file che vuoi aprire.

Di solito utilizziamo un percorso relativo, che indica la posizione del file rispetto a quella dello script (file Python) che sta chiamando la funzione open().

Ad esempio, il percorso in questa chiamata di funzione è:

open("names.txt") # Il percorso relativo è "names.txt"

Contiene solo il nome del file e può essere utilizzato quando il file che stai cercando di aprire è nella stessa cartella dello script Python, così:

image-7

Se invece il file si trova in una cartella annidata:

image-9
Il file names.txt è nella cartella "data"

dobbiamo usare un percorso specifico per comunicare alla funzione che il file si trova in un'altra cartella.

In quest'esempio, il percorso è:

open("data/names.txt")

Nota che stiamo scrivendo prima data/ (il nome della cartella seguito da /) e poi names.txt (il nome del file con l'estensione).

💡 Tip: le tre lettere .txt che seguono il punto in names.txt costituiscono l'estensione del file o il tipo. In questo caso, .txt indica un file di testo.

Secondo parametro: mode

Il secondo parametro della funzione open() è mode (modalità), una stringa di un solo carattere. In pratica, questo carattere dice a Python cosa vuoi fare nel tuo programma con il file.

Le modalità disponibili sono:

  • Lettura ("r", read)
  • Aggiunta ("a", append)
  • Scrittura ("w", write)
  • Creazione ("x", create)

Puoi anche scegliere di aprire un file in:

  • Modalità di testo ("t")
  • Modalità binaria ("b")

Per usare le modalità binaria o di testo, avrai bisogno di aggiungere questi caratteri a quello principale. Ad esempio:"wb" significa scrittura in modalità binaria.

💡 Tip: le modalità di default sono scrittura ("r") e testo ("t"), che significa "apri per leggere il testo" ("rt"), quindi non hai bisogno di specificarle in open(), dato che sono predefinite. Se vuoi utilizzarle puoi semplicemente scrivere open(<file>).

Perché le modalità?

Sembra piuttosto sensato che Python garantisca solo certe autorizzazioni a seconda di ciò che desideri fare con il file, giusto? Perché Python dovrebbe permettere al tuo programma di fare più del necessario? Sostanzialmente è questo il motivo per cui le modalità esistono.

Pensaci: permettere a un programma di fare più del necessario può essere problematico. Ad esempio, se hai bisogno di leggere il contenuto di un file, può essere deleterio permettere al tuo programma di modificarlo inaspettatamente, introducendo potenziali bug.

🔸 Come leggere un file

Ora che ne sai di più sugli argomenti che accetta la funzione open(), vediamo come puoi aprire un file e memorizzarlo in una variabile per usarlo nel tuo programma.

Ecco la sintassi di base:

image-41
<var> rappresenta la variabile in cui memorizzare l'oggetto file.

Stiamo semplicemente assegnando il valore restituito a una variabile. Ad esempio:

names_file = open("data/names.txt", "r")

Immagino che tu ti stia chiedendo: che tipo di valore viene restituito da open()?

Bene, si tratta di un oggetto file.

Parliamone un po'.

Oggetti file

Secondo la documentazione Python, un oggetto file è:

Un oggetto che possiede una API orientata ai file (con metodi come read() o write()) per una risorsa di base.

Questo vuol dire che un oggetto file è un oggetto che ci permette di lavorare e interagire con dei file esistenti all'interno del nostro programma Python.

Gli oggetti file hanno attributi, come:

  • name: il nome del file.
  • closed: True Se il file è chiuso. False altrimenti.
  • mode: la modalità usata per aprire il file.
image-57

Ad esempio:

f = open("data/names.txt", "a")
print(f.mode) # Output: "a"

Adesso vediamo come puoi accedere al contenuto di un file attraverso un oggetto file.

Metodi per leggere un file

Per essere in grado di lavorare con degli oggetti file, abbiamo bisogno di "interagire" con loro nel nostro programma, ed è esattamente ciò che fanno i metodi. Vediamone alcuni.

Read()

Il primo metodo che devi conoscere è read(), che restituisce l'intero contenuto del file come una stringa.

image-11

Ecco un esempio:

f = open("data/names.txt")
print(f.read())

L'output è:

Nora
Gino
Timmy
William

Puoi usare la funzione type() per verificare che il valore restituito da f.read() è una stringa:

print(type(f.read()))

# Output
<class 'str'>

Sì, è una stringa!

In questo caso, l'intero file è stato stampato perché non abbiamo specificato un numero massimo di byte, ma potremmo farlo.

Come in quest'esempio:

f = open("data/names.txt")
print(f.read(3))

Il valore restituito è limitato dal numero di byte:

Nor

❗️Importante: devi chiudere un file dopo aver terminato di utilizzarlo per liberare le risorse associate al file. Per farlo, devi chiamare il metodo close(), in questo modo:

image-22

Readline() vs. Readlines()

Con questi due metodi, puoi leggere un file riga per riga. Sono leggermente diversi, quindi vediamoli nel dettaglio.

readline() legge una riga del file finché raggiunge la fine della riga. Un carattere finale "nuova riga" (\n) viene mantenuto nella stringa.

💡 Tip: puoi specificare facoltativamente il numero massimo di caratteri (size) che vuoi includere nella stringa risultante.

image-19

Ad esempio:

f = open("data/names.txt")
print(f.readline())
f.close()

L'output è:

Nora

Questa è la prima riga del file.

readlines(), invece, restituisce una lista con tutte le righe del file come elementi individuali (stringhe). Ecco la sintassi:

image-21

Ad esempio:

f = open("data/names.txt")
print(f.readlines())
f.close()

L'output è:

['Nora\n', 'Gino\n', 'Timmy\n', 'William']

Nota che il carattere \n è presente alla fine di ogni stringa, eccetto l'ultima.

💡 Tip: puoi ottenere la stessa lista con list(f).

Puoi lavorare con questa lista nel tuo programma, assegnandola a una variabile o usandola in un loop:

f = open("data/names.txt")

for line in f.readlines():
    # Fai qualcosa per ogni riga
    
f.close()

Possiamo anche iterare direttamente su f (l'oggetto file) in un loop:

f = open("data/names.txt", "r")

for line in f:
	# Fai qualcosa per ogni riga

f.close()

Questi sono i metodi principali che vengono usati per leggere gli oggetti file. Adesso vedremo come creare dei file.

🔹 Come creare un file

Se hai bisogno di creare un file "dinamicamente" usando Python, puoi farlo in modalità "x". Vediamo come.

Ecco la sintassi di base:

image-58

In questo esempio, questa è la mia cartella di lavoro:

image-29

Eseguendo questa riga di codice:

f = open("new_file.txt", "x")

viene creato un nuovo file con il nome new_file.txt.

image-30

In questa modalità, puoi creare un file e scrivere dinamicamente al suo interno usando i metodi che imparerai tra poco.

💡 Tip: il file sarà inizialmente vuoto finché non lo modifichi.

Se provi ad eseguire questa riga di codice ed esiste già un file con quel nome, otterrai questo messaggio di errore:

Traceback (most recent call last):
  File "<path>", line 8, in <module>
    f = open("new_file.txt", "x")
FileExistsError: [Errno 17] File exists: 'new_file.txt'

Secondo la documentazione Python, questa eccezione (errore di runtime) è:

Generata quando si cerca di creare un file o una cartella che esiste già.

Ora che sai come creare un file, vediamo insieme come modificarlo.

🔸 Come modificare un file

Per modificare un file (scrivere al suo interno), hai bisogno del metodo write(). Ci sono due modi per farlo (append o write), a seconda della modalità con cui scegli di aprirlo. Vediamoli nel dettaglio.

Append

"Append" significa aggiungere qualcosa alla fine di qualcos'altro. La modalità "a" ti permette di aprire un file e aggiungere del contenuto alla sua fine.

Ad esempio, se abbiamo questo file:

image-43

E vogliamo aggiungergli una nuova riga, possiamo aprirlo in modalità "a" (append) e poi chiamare il metodo write(), passando il contenuto che vogliamo aggiungere come argomento.

Questa è la sintassi di base per chiamare il metodo write():

image-52
<string> rappresenta il contenuto da aggiungere alla fine del file.

Ecco un esempio:

f = open("data/names.txt", "a")
f.write("\nNew Line")
f.close()

💡 Tip: Nota che sto aggiungendo \n prima della riga per indicare che voglio che la nuova riga appaia separatamente e non come prosecuzione di quella esistente.

Questo è il file dopo aver eseguito lo script:

image-45

💡 Tip: la nuova riga potrebbe non essere visualizzata finché non viene eseguito f.close().

Write

Delle volte potresti desiderare di cancellare il contenuto di un file e sostituirlo interamente con del nuovo contenuto. Per questa operazione puoi usare il metodo write() se apri il file in modalità "w".

Consideriamo questo file:

image-43

Dopo aver eseguito lo script:

f = open("data/names.txt", "w")
f.write("New Content")
f.close()

Otterremo questo risultato:

image-46

Come puoi vedere, aprire un file in modalità "w" per poi scrivere, sostituisce il contenuto esistente.

💡 Tip: il metodo write() restituisce il numero dei caratteri scritti.

Se vuoi scrivere diverse righe in un colpo solo, puoi usare il metodo writelines(), che accetta una lista di stringhe, ognuna corrispondente a una riga da aggiungere al file.

Vediamo un esempio, partendo dal file iniziale:

image-43

Eseguiamo lo script:

f = open("data/names.txt", "a")
f.writelines(["\nline1", "\nline2", "\nline3"])
f.close()

Le righe vengono aggiunte alla fine del file:

image-47

Aprire un file per operazioni multiple

Adesso sai come creare, leggere e scrivere un file, ma se volessi fare più cose nello stesso programma? Vediamo cosa accade se cerchiamo di farlo con le modalità che abbiamo imparato finora.

Se apri un file in modalità "r" (read) e poi provi a scrivere:

f = open("data/names.txt")
f.write("New Content") # Prova a scrivere
f.close()

Otterrai quest'errore:

Traceback (most recent call last):
  File "<path>", line 9, in <module>
    f.write("New Content")
io.UnsupportedOperation: not writable

Analogamente, se apri un file in modalità "w" (write) e poi provi a leggerlo:

f = open("data/names.txt", "w")
print(f.readlines()) # Prova a leggere
f.write("New Content")
f.close()

Otterrai l'errore:

Traceback (most recent call last):
  File "<path>", line 14, in <module>
    print(f.readlines())
io.UnsupportedOperation: not readable

Accade lo stesso in modalità "a" (append).

Come fare quindi? Per essere in grado di leggere un file e eseguire altre operazioni nello stesso programma, devi aggiungere il simbolo "+" alla modalità, in questo modo:

f = open("data/names.txt", "w+") # Read + Write
f = open("data/names.txt", "a+") # Read + Append
f = open("data/names.txt", "r+") # Read + Write

Molto utile, vero? Questo è probabilmente ciò che utilizzerai nei tuoi programmi, ma assicurati di includere solo le modalità di cui hai bisogno per evitare potenziali bug.

A volte i file non sono più necessari. Vediamo come cancellarli usando Python.

🔹 Come cancellare un file

Per eliminare un file usando Python, devi importare un modulo chiamato os che contiene delle funzioni che interagiscono con il sistema operativo.

💡 Tip: un modulo è un file Python con variabili, funzioni e classi.

In particolare, hai bisogno della funzione remove(), che accetta il percorso del file come argomento e cancella il file automaticamente.

image-56

Se vogliamo rimuovere il file chiamato sample_file.txt

image-34

Possiamo farlo con questa riga di codice:

import os
os.remove("sample_file.txt")
  • Prima riga: l'istruzione import import os viene scritta in cima al file e ti permette di accedere alle funzioni definite nel modulo os.
  • Seconda riga: os.remove("sample_file.txt") rimuove il file specificato.

💡 Tip: puoi usare un percorso assoluto o relativo.

Ora che sai come cancellare un file, parliamo di uno strumento interessante... Un gestore di contesto!

🔸 Gestori di contesto

I gestori di contesto sono dei costrutti Python che ti semplificheranno la vita. Usandoli, non hai bisogno di ricordarti di chiudere un file alla fine del tuo programma e puoi avere accesso al file in una parte specifica del programma.

Sintassi

Questo è un esempio di un gestore di contesto usato per lavorare con dei file:

image-33

💡 Tip: il corpo del gestore di contesto deve essere indentato, proprio come per loop, funzioni e classi. Se il codice non è indentato, non verrà considerato parte del gestore di contesto.

Quando il corpo del gestore di contesto è terminato, il file si chiude automaticamente.

with open("<path>", "<mode>") as <var>:
    # Lavorando sul file...

# Il file viene chiuso!

Esempio

with open("data/names.txt", "r+") as f:
    print(f.readlines()) 

Questo gestore di contesto apre il file names.txt per operazioni di lettura/scrittura e assegna l'oggetto file alla variabile f. Questa variabile verrà usata nel corpo del gestore di contesto per fare riferimento all'oggetto file.

Provare a leggere di nuovo il file

Una volta che il corpo è stato completato, il file viene chiuso automaticamente, quindi non può essere letto senza essere prima riaperto. Ma aspetta! Abbiamo una riga di codice che cerca di leggerlo ancora, proprio qui sotto:

with open("data/names.txt", "r+") as f:
    print(f.readlines())

print(f.readlines()) # Prova a leggere di nuovo il file, fuori dal gestore di contesto

Vediamo cosa succede:

Traceback (most recent call last):
  File "<path>", line 21, in <module>
    print(f.readlines())
ValueError: I/O operation on closed file.

Otteniamo quest'errore perché stiamo cercando di leggere un file chiuso. Fantastico, non è vero? Il gestore di contesto fa tutto il lavoro duro per noi, in modo chiaro e conciso.

🔹 Come gestire le eccezioni lavorando con i file

Avendo a che fare con i file, può capitare di ottenere degli errori. A volte potresti non avere le autorizzazioni necessarie per modificare o accedere a un file oppure il file che vuoi usare potrebbe addirittura non esistere.

Come programmatore, hai bisogno di prevedere queste circostanze e gestirle nel tuo programma per evitare crash improvvisi, che potrebbero compromettere l'esperienza dell'utente.

Vediamo alcune delle eccezioni (errori di runtime) più comuni che potresti incontrare lavorando con dei file.

FileNotFoundError

Secondo la documentazione Python, questa eccezione:

Si verifica quando un file o una cartella vengono richiesti ma non esistono.

Ad esempio, se il file che stai cercando di aprire non esiste nella cartella in cui ti trovi:

f = open("names.txt")

Otterrai quest'errore:

Traceback (most recent call last):
  File "<path>", line 8, in <module>
    f = open("names.txt")
FileNotFoundError: [Errno 2] No such file or directory: 'names.txt'

Analizziamo quest'errore riga per riga:

  • File "<path>", line 8, in <module>. Questa riga ti dice che l'errore è stato causato durante l'esecuzione del codice del file situato in <path>. In modo specifico, durante l'esecuzione di line 8 in <module>.
  • f = open("names.txt"). Questa è la riga che ha causato l'errore.
  • FileNotFoundError: [Errno 2] No such file or directory: 'names.txt' . Questa riga dice che l'eccezione FileNotFoundError è stata generata perché il file (o la cartella) names.txt non esiste.

💡 Tip: Python è molto descrittivo con i messaggi di errore e questo è un grande vantaggio se stai cercando di eliminare dei bug.

PermissionError

Questa è un'altra eccezione comune lavorando con i file. Secondo la documentazione Python, questa eccezione:

Si verifica quando si prova a eseguire un'operazione senza avere i requisiti d'accesso adeguati (ad esempio, autorizzazioni filesystem).

Questa eccezione viene generata quando stai cercando di leggere o modificare un file per cui non possiedi l'autorizzazione di accesso. In questo caso, vedrai il seguente errore:

Traceback (most recent call last):
  File "<path>", line 8, in <module>
    f = open("<file_path>")
PermissionError: [Errno 13] Permission denied: 'data'

IsADirectoryError

Secondo la documentazione Python, questa eccezione:

Si verifica quando un'operazione su un file viene richiesta su una cartella.

Questa particolare eccezione viene causata tentando di aprire o lavorare su una cartella invece di un file, quindi stai attento al percorso che passi come argomento.

Come gestire le eccezioni

Per gestire queste eccezioni, puoi usare l'istruzione try/except. Con questa istruzione puoi dire al programma cosa fare se dovesse accadere qualcosa di inatteso.

Questa è la sintassi di base:

try:
	# Prova a eseguire questo codice
except <type_of_exception>:
	# Se si verifica un'eccezione di questo tipo, stoppa il processo e passa a questo blocco di codice
    

Qui puoi vedere un esempio con FileNotFoundError:

try:
    f = open("names.txt")
except FileNotFoundError:
    print("The file doesn't exist")

Questo blocco di codice, in pratica, dice:

  • Prova ad aprire il file names.txt.
  • Se ottieni FileNotFoundError, non andare in crash! Restituisci una frase descrittiva per l'utente.

💡 Tip: puoi scegliere di gestire la situazione scrivendo il codice appropriato nel blocco except. Forse potresti creare un nuovo file se quello che ti interessa non esiste ancora.

Per chiudere automaticamente il file dopo l'operazione (indipendentemente dalla presenza di un'eccezione o meno nel blocco try) puoi aggiungere il blocco finally.

try:
	# Prova a eseguire questo codice
except <exception>:
	# Se si verifica questa eccezione, stoppa subito il processo e passa a questa riga di codice
finally: 
	# Fai questo dopo aver eseguito il codice, anche se si verifica un'eccezione

Ecco un esempio:

try:
    f = open("names.txt")
except FileNotFoundError:
    print("The file doesn't exist")
finally:
    f.close()

Ci sono molti modi per personalizzare le istruzioni try/except/finally a puoi anche aggiungere un blocco else per eseguire un blocco di codice solo se non ottieni un'eccezione nel blocco try.

💡 Tip: per imparare di più sulla gestione delle eccezioni in Python, puoi leggere il mio articolo: "How to Handle Exceptions in Python: A Detailed Visual Introduction".

🔸 In conclusione

  • Puoi creare, leggere, scrivere e cancellare un file usando Python.
  • Gli oggetti file hanno i propri set di metodi che puoi usare all'interno del tuo programma.
  • I gestori di contesto ti aiutano a lavorare con i file e a gestirli chiudendoli automaticamente quando hai completato un'operazione.
  • La gestione delle eccezioni è cruciale in Python. Le eccezioni comuni che riguardano i file sono FileNotFoundError, PermissionError e IsADirectoryError e possono essere gestite con try/except/else/finally.

Spero davvero che quest'articolo ti sia piaciuto e che ti sia stato d'aiuto. Adesso puoi lavorare con i file nei tuoi progetti Python.⭐️