4.11. Oggetti file

Python ha una funzione built-in, open, per aprire un file su disco. open ritorna un oggetto di tipo file, che dispone di metodi e attributi per ottenere informazioni sul file aperto e manipolarlo.

Esempio 4.23. Aprire un file

>>> f = open("/music/_singles/kairo.mp3", "rb") 1
>>> f                                           2
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.mode                                      3
'rb'
>>> f.name                                      4
'/music/_singles/kairo.mp3'
1 Il metodo open può prendere fino a tre parametri: il nome del file, una modalità di apertura e una dimensione di buffer. Solo il primo, il nome, è obbligatorio; gli altri due sono opzionali. Se non è specificato, il file è aperto per la lettura in modo testo. In questo esempio apriamo il file per la lettura in binario (print open.__doc__ visualizza la spiegazione completa di tutte le modalità possibili).
2 La funzione open ritorna un oggetto (a questo punto non dovrebbe più essere una sorpresa per voi). Un oggetto file ha molti utili attributi.
3 L'attributo mode di un oggetto file ritorna la modalità di apertura del file.
4 L'attributo name di un oggetto file ritorna il nome del file con cui è stato aperto.

Esempio 4.24. Leggere un file

>>> f
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.tell()              1
0
>>> f.seek(-128, 2)       2
>>> f.tell()              3
7542909
>>> tagData = f.read(128) 4
>>> tagData
'TAGKAIRO****THE BEST GOA         ***DJ MARY-JANE***            Rave Mix                      2000http://mp3.com/DJMARYJANE     \037'
>>> f.tell()              5
7543037
1 Un oggetto file contiene lo stato del file con cui è stato creato. Il metodo tell ritorna la posizione corrente all'interno del file. Poiché non abbiamo ancora fatto nulla con questo file, la posizione corrente è 0, ovvero l'inizio del file.
2 Il metodo seek di un oggetto file sposta la posizione corrente all'interno del file. Il secondo parametro specifica il significato del primo; 0 significa “spostati a una posizione assoluta” (contando dall'inizio del file), 1 significa che il primo parametro è relativo alla posizione attuale, e 2 significa “spostati relativamente dalla fine del file”. Poiché i tag MP3 che stiamo cercando sono memorizzati alla fine del file, usiamo 2 e diciamo all'oggetto file di spostare la posizione attuale a 128 byte prima della fine del file.
3 Il metodo tell conferma che la posizione corrente all'interno del file è cambiata.
4 Il metodo read legge un numero specificato di byte dal file aperto e ritorna una stringa contenente i dati che sono stati letti. Il parametro opzionale specifica il massimo numero di byte da leggere. In assenza di questo parametro, il metodo read legge il file fino alla fine. (Avremmo potuto scrivere semplicemente read() qui, poiché sappiamo esattamente qual'è la posizione corrente nel file e stiamo, in effetti, leggendo gli ultimi 128 byte.) I dati letti sono assegnati alla variabile tagData e la posizione corrente viene aggiornata.
5 Il metodo tell conferma che la posizione corrente è stata aggiornata. Se fate i calcoli, vedrete che dopo aver letto 128 byte, la posizione è avanzata di 128.

Esempio 4.25. Chiudere un file

>>> f
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.closed  1
0
>>> f.close() 2
>>> f
<closed file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.closed
1
>>> f.seek(0) 3
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.tell()
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.read()
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.close() 4
1 L'attributo closed di un oggetto file indica se l'oggetto ha un file aperto oppure no. In questo caso, il file è ancora aperto (closed vale 0). I file aperti consumano risorse di sistema e a seconda della modalità di apertura, gli altri programmi potrebbero non essere in grado di accedervi. È importante chiudere i file appena avete finito di usarli.
2 Per chiudere un file, chiamate il metodo close dell'oggetto file. Questo metodo libera il lock (se presente) che state tenendo sul file, svuota i buffer di scrittura (se presenti) che il sistema non ha ancora scritto su disco e rilascia le risorse di sistema. L'attributo closed conferma che il file è stato chiuso.
3 Solo perché il file è stato chiuso, non significa che l'oggetto file non esista più. La variabile f continua ad esistere finché non esce dallo scope corrente o è cancellata manualmente. In ogni caso, nessuno dei metodi che manipolano un file funzionerà una volta che il file è stato chiuso, ma solleveranno un'eccezione.
4 Chiamare close su un oggetto il cui file è già stato chiuso non genera un'eccezione; fallisce silenziosamente.

Esempio 4.26. Oggetti file in MP3FileInfo

        try:                                1
            fsock = open(filename, "rb", 0) 2
            try:                           
                fsock.seek(-128, 2)         3
                tagdata = fsock.read(128)   4
            finally:                        5
                fsock.close()              
            .
            .
            .
        except IOError:                     6
            pass                           
1 Poiché aprire e leggere un file è un'operazione che potrebbe generare un'eccezione, tutto il codice è racchiuso in un blocco try...except (e qui dovreste cominciare ad apprezzare le indentazioni standard di Python...).
2 La funzione open può generare un IOError (forse il file non esiste).
3 Il metodo seek può generare un IOError (forse il file è più piccolo di 128 byte).
4 Il metodo read può generare un IOError (forse il disco ha un settore illeggibile, o è un disco di rete e la rete non è disponibile).
5 Questo è nuovo: un blocco try...finally. Una volta che il file è stato aperto con successo dalla funzione open, vogliamo essere assolutamente sicuri che lo chiuderemo, anche se venisse generata un'eccezione dai metodi seek o read. A questo serve il blocco try...finally: il codice nel blocco finally verrà sempre eseguito, anche se qualcosa nel blocco try generasse un'eccezione. Pensate al blocco finally come a codice che viene eseguito “all'uscita”, indipendentemente da ciò che è successo prima.
6 Infine, gestiamo la nostra eccezione IOError che potrebbe essere generata dalla chiamata a open, seek, o read. Qui non fa differenza, perché ci limitiamo ad ignorarla e continuare. Ricordate, pass è un comando Python che non fa nulla. Questo è perfettamente lecito; “gestire” un'eccezione può significare esplicitamente non fare nulla. L'eccezione verrà comunque considerata gestita e il programma continuerà normalmente con la riga di codice successiva al blocco try...except.

Ulteriori letture