Una comune operazione compiuta da molti programmi che usiamo sul computer è il salvataggio ed il caricamento di dati dal disco rigido. Queste funzionalità sono supportate da tutti i linguaggi di programmazione, che in genere implementano un file system, cioè un insieme di istruzioni atte a questo scopo.
In Python esiste un tipo di dato apposito, il tipo file: una volta creata una variabile di questo tipo, potremo usarla per le nostre operazioni.
Per creare un oggetto di tipo file è necessaria la funzione open()
: essa va chiamata con due
argomenti: il primo è una stringa che contiene il nome del file che vogliamo aprire, il secondo (opzionale)
indica le operazioni che vogliamo compiere sul file. Proviamo con IDLE:
>>> f = open("miofile.tmp", "w")
Se eseguite questa riga di codice ed andate poi ad esplorare la directory nella quale è installato Python (in Windows dovrebbe essere C:\Python34 o C:\Programmi\Python34) dovreste trovare il file miofile.tmp creato da Python.
Il significato della seconda stringa è il seguente:
Valore | Significato | |
---|---|---|
"w" | write | Apre un file in scrittura: se il file con il nome indicato non è presente sul disco ne crea uno, altrimenti cancella i contenuti del file e li sovrascrive con i dati che scriveremo |
"r" | read | Apre un file in lettura. Il file con il nome indicato deve essere già presente sul disco, altrimenti si ottiene un FileNotFoundError |
"a" | append | Apre un file per aggiungere dati: se il file con il nome indicato non è presente sul disco ne crea uno, altrimenti scriverà i nuovi dati alla fine del file, senza cancellare quelli vecchi |
"r+" | read - write | Apre un file sul quale potremo sia scrivere che leggere. Anche in questo caso il file deve essere già esistente, o si ottiene un errore |
il secondo argomento è opzionale ed il suo valore di default è "r" (in modo da non cancellare accidentalmente un file se ce lo dimentichiamo!).
Purtroppo la trattazione non finisce qui, perchè ai valori indicati possiamo aggiungere una "b"
alla fine ("rb", "wb", "ab", "r+b"
) dove la b significa binary.
Il significato di questa b è piuttosto oscuro e dovuto al fatto che Windows, Linux e Mac scrivono e leggono
le stringhe con formati diversi. In pratica non dovremo mettere la b se intendiamo scrivere e leggere soltanto
stringhe (modalità testo), mentre dovremo metterla se intendiamo scrivere e leggere
dati di altro tipo (modalità binaria). Anche così è però
probabile che una stringa scritta da un computer Windows non sia poi leggibile da un Mac. Se tutto questo vi
sembra troppo complicato, sappiate che per fortuna Python ha trovato un modo per aggirare il problema, che
spiegheremo alla fine, e che quindi potete per il momento non preoccuparvi se non avete capito. In ogni caso nel
prossimo paragrafo mostreremo alcune semplici operazioni di scrittura e lettura di stringhe, quindi apriremo
i file in modalità testo.
Una volta aperto il file, il nostro programma vedrà l'oggetto file come uno stream
(flusso di caratteri), cioè una specie di grossa stringa nella quale leggere o scrivere caratteri in
sequenza. All'interno di questo stream le varie stringhe sono delimitate dal carattere
di ritorno a capo "\n"
e si può leggerle e scriverle mediante le seguenti funzioni, che
usano tutte la sintassi con il punto (nella tabella seguente f
è un oggetto di tipo file, che
supponiamo già creato con la open()
).
Funzione | Significato |
---|---|
f.read() |
restituisce l'intero contenuto del file sotto forma di una sola stringa. Se il file era composto da
più stringhe, esse saranno separate dal carattere di ritorno a capo "\n" |
f.read(n) |
come la precedente, ma legge al massimo n caratteri (se il file è più lungo si ferma) |
f.readline() |
legge e restituisce la prossima stringa dal file (cioè legge il file solo fino al prossimo carattere
"\n" ). Se siamo arrivati alla fine del file restituisce la stringa nulla. |
f.readline(n) |
legge e restituisce la prossima stringa, fino ad un massimo di n caratteri |
f.write(str) |
scrive una stringa nel file. Se vogliamo terminare la stringa è necessario aggiungere alla fine il
carattere "\n" , altrimenti la prossima scrittura sarà la continuazione di questa stringa.
Restituisce il numero di caratteri scritti. |
f.close() |
chiude il file. E' sempre bene chiudere i file appena abbiamo finito di leggere o scrivere, in quanto essi richiedono risorse del sistema operativo (in ogni caso all'uscita dal programma tutti i file vengono chiusi automaticamente). Dopo aver chiuso un oggetto file non possiamo più usarlo per leggere o scrivere (otterremmo un errore). |
Vediamo ora di fare qualche esempio in IDLE: ricordatevi che i nostri esempi lasceranno poi come "spazzatura" il nostro file di prova nella directory di IDLE, quindi eventualmente cercatelo e cancellatelo.
Per prima cosa creiamo un file e scriviamoci qualcosa (notiamo che quando usiamo la write()
IDLE
ci risponde come al solito scrivendo il valore restituito, cioè il numero di caratteri scritti nel file):
>>> f = open("miofile.tmp", "w") # file aperto in scrittura >>> f.write("Voglio tanto bene a mamma\n") # ritorno a capo alla fine della stringa
26
>>> f.write("Ma veramente tanto! ") # senza ritorno a capo
20
>>> f.write("Tanto tanto!\n")
13
>>> f.close() >>> f.write("Non posso piu' scrivere!")
Traceback (most recent call last): File "<pyshell#9>", line 1, in <module> f.write("Non posso piu' scrivere!") ValueError: I/O operation on closed file.
Alla fine ho volutamente provocato un errore cercando di scrivere dopo la chiusura. Ora riapriamo il nostro file in lettura e rileggiamo quello che vi avevamo scritto:
>>> f = open("miofile.tmp", "r") >>> f.read()
'Voglio tanto bene a mamma\nMa veramente tanto! Tanto tanto!\n'
Notiamo che la seconda e la terza write()
hanno in realtà scritto una sola stringa
'Ma veramente tanto! Tanto tanto!\n'
, perchè nella prima delle due non avevamo aggiunto il ritorno
a capo. Se ora proviamo ancora a leggere dal file, otterremo delle stringhe vuote, perchè abbiamo raggiunto la
fine del file (Inglese: EOF, end of file). Se vogliamo leggere le stringhe una alla
volta dobbiamo chiudere e riaprire:
readline() # ho gia' letto tutto, non posso piu' leggere
''
>>> f.close() # chiudo e riapro >>> f = open("miofile.tmp", "r") >>> f.readline()
'Voglio tanto bene a mamma\n'
>>> f.readline()
'Ma veramente tanto! Tanto tanto!\n'
Sorge ora una domanda: cosa dovrei fare se volessi salvare dei numeri (interi o float)? Un semplice
metodo è quello di convertirli in stringhe con la funzione str()
. Vediamo un esempio:
>>> f.close() >>> f = open("miofile.tmp", "a") # apre il file per aggiungere dati alla fine >>> f.write(str(10)+ "\n") # scrive il numero 10 sotto forma di stringa
3
>>> f.close() # chiudiamo il file ... >>> f = open("miofile.tmp", "r") # ... e lo riapriamo per leggere >>> f.readline() # le due righe gia' scritte ...
'Voglio tanto bene a mamma\n'
>>> f.readline()
'Ma veramente tanto! Tanto tanto!\n'
>>> n = int(f.readline()) # ,,, e il nosto numero (riconvertito da stringa ad intero) >>> n
10
Questo metodo è comunque piuttosto farraginoso: se dovessimo salvare un oggetto piuttosto complesso come una lista (o una lista di liste) ci servirebbero molte istruzioni di conversione. Inoltre rimane il fatto accennato prima che ad esempio un computer Mac potrebbe non leggere le stringhe scritte da un computer Windows.
Per risolvere questi problemi i programmatori di Python hanno deciso di intraprendere una via piuttosto radicale, cioè di salvare i dati su disco in un formato proprio di Python. Le funzioni per leggere e scrivere in questo formato sono contenute nel modulo pickle. Le più utilizzate sono le seguenti:
Funzione | Significato |
---|---|
dump(obj, file) |
scrive la variabile obj nel file file , che deve essere stato aperto in
modalità binaria. obj può essere una variabile di qualsiasi
tipo (stringa, numero, lista ...) |
load(file) |
restituisce un oggetto letto dal file file (anch'esso aperto in modalità binaria) |
Queste due funzioni ci semplificano notevolmente la vita. Basterà ricordarsi di aprire i file in modalità binaria ("wb", "rb", "ab", "r+b"), dopodichè potremo salvare o caricare qualsiasi cosa con una singola istruzione. Notate anche che esse, a differenza di quelle precedenti, non usano la sintassi con il punto e vanno chiamate mettendo il nome del file tra gli argomenti.
Vediamo un'applicazione: riprendiamo il nostro vecchio gioco indovina_numerp.py e modifichiamolo in modo che ricordi il record ed il nome di chi l'ha stabilito:
from random import *
from pickle import *
try:
f = open("records.dat", "rb") # apre il file "records.dat"
record = load(f) # carica il record
nome = load(f) # carica il nome del giocatore
f.close()
except FileNotFoundError:
record = 1000000000 # se il file non esiste inizializza come prima
nome = ""
risp = "s" # da qui il programma prosegue come prima
while risp == "s":
num = randrange(1, 21)
tentativi = 0
print ("Ho pensato un numero da 1 a 20. Prova ad indovinarlo!")
mio_num = -1
while mio_num != num:
tentativi += 1
mio_num = int(input("??? "))
if mio_num < num:
print("Troppo basso")
elif mio_num > num:
print("Troppo alto")
print("Hai indovinato! Hai impiegato", tentativi, "tentativi")
if tentativi < record:
print("Hai battuto il record!")
record = tentativi
nome = input("Inserisci il tuo nome: ")
# chiede anche il nome del giocatore
risp = input("Vuoi giocare ancora (s/n)? ")
# uscita dal programma
f = open("records.dat", "wb") # riapre il file in scrittura ...
dump(record, f) # ... salva il record ...
dump(nome, f) # ... ed il nome
f.close()
Soffermiamoci un attimo sulle prime righe: notate che l'istruzione open()
è inserita
in una try
: questo è un comune stratagemma per gestire la possibilità che il file non
esista (ad esempio la prima volta che si lancia il programma). Se il file non esiste verrà eseguita la
except
ed inizializzeremo le nostre variabili record
e nome
come
facevamo prima, altrimenti le caricheremo da file. Alla fine del programma riapriamo lo stesso file
in scrittura e salviamo nuovamente le due variabili, in modo che esse siano disponibili la prossima volta
che lanciamo il programma.
Fine della lezione