Come trattare file e testi

Contenuto del capitolo
  • Come aprire un file
  • Come leggere e scrivere un file aperto
  • Come chiudere un file
  • Costruire un contatore di parole

Le operazioni sui file spesso costituiscono per i principianti un problema, anche se non riesco a spiegarmi perché. I file dal punto di vista della programmazione non sono affatto differenti da quelli che si usano con un programma di elaborazione di testi o con un'altra applicazione: devono essere aperti (open, N.d.t.), poi è possibile fare le operazioni e infine devono essere chiusi (close, N.d.t.).

La differenza maggiore consiste nel fatto che da un programma l'accesso avviene in modo sequenziale, cioè si legge una linea alla volta a partire dall'inizio. In pratica l'elaboratore di testi spesso si comporta allo stesso modo, solo che scrive l'intero file in memoria mentre lo si usa e lo riscrive completamente al termine, quando viene chiuso. Un'altra differenza consiste nel fatto che normalmente il file viene aperto o in sola lettura o in sola scrittura. Si può scrivere in un file creandone uno nuovo (o sovrascrivendo uno già esistente) oppure appendendo linee ad un file esistente.

Un'altra cosa che può essere fatta con un file durante il suo uso è tornare all'inizio.

Files - Ingresso ed Uscita

Passiamo alla pratica. Presupporremo che esista un file di nome menu.txt che contenga un elenco di piatti:
prosciutto e uova
prosciutto e patatine
prosciutto e prosciutto

Adesso scriveremo un programma che legge il file e ne visualizza il contenuto, come il comando 'cat' di Unix o 'type' del DOS.

# Prima apriamo il file in lettura (r)
inp = open("menu.txt","r")
# Si trasferisce il contenuto del file in una lista
# e poi si scrive ogni elemento di questa
for linea in inp.readlines():
    print linea
# Adesso il file viene chiuso
inp.close()

Nota 1: open() richiede due argomenti. Il primo è il nome del file (che può essere passato coma una variabile oppure come una costante di tipo stringa, come abbiamo fatto nell'esempio). Il secondo è il modo. Il modo stabilisce se stiamo aprendo il file per leggere (r) o per scrivere (w), e anche se il file contiene testo ASCII o dati di tipo binario - aggiungendo in questo caso una 'b' ad 'r' o a 'w', ad esempio: open(fn,"rb")

Nota 2: Abbiamo letto e chiuso il file usando funzioni precedute dalla variabile relativa al file. Questa notazione viene detta chiamata di metodo ed è un primo esempio di programmazione ad oggetti. Per il momento non ce ne preoccupiamo se non per capire che è in qualche modo collegata ai moduli. Possiamo pensare la variabile collegata al file come un riferimento ad un modulo che contiene le funzioni per le operazioni sui file e che viene importato automaticamente ogni volta che creiamo una variabile di tipo file.

Vediamo come possiamo trattare i file lunghi. Innanzitutto sarà necessario leggere il file una linea alla volta (in Python si usa la funzione readline() invece di readlines()). Si può quindi usare una variabile NumeroLinee che viene incrementata ad ogni linea e verificata ogni volta per vedere se è uguale a 25 (per un schermo a 25 linee). Quando è uguale si chiede all'utilizzatore di premere un tasto (ad esempio invio) prima di rimettere NumeroLinee a zero e proseguire. Potreste provare a realizzare il programma per esercizio...

In effetti non c'è altro. Si apre un file, lo si legge, e poi lo si manipola in qualunque modo desiderato. Al termine si chiude il file. Per creare un comando 'copy' (copia, N.d.t.) in Python semplicemente apriamo un nuovo file in scrittura e scriviamo le linee su tale file invece che scriverle sullo schermo. Ad esempio:

# Creare l'equivalente del comando: COPY MENU.TXT MENU.BAK

# Prima di tutto si aprono i file in lettura e scrittura
inp = open("menu.txt","r")
outp = open("menu.bak","w")

# si legge il file in una lista e si copia
# nel file nuovo
for line in inp.readlines():
    outp.write(line)

print "copiato 1 file..."

# Adesso si chiudono i file
inp.close()
outp.close()

Come avrete notato abbiamo aggiunto una istruzione print al solo scopo di assicurare all'utilizzatore che è effettivamente accaduto qualcosa. Usare questo tipo di messaggi di controllo è sempre una buona idea.

E per finire un'altra variante: supponiamo che vogliate appendere dati ad un file già esistente. Un modo per farlo potrebbe essere aprire il file in lettura, leggere i dati nella lista, appendere altri dati alla lista ed infine scrivere l'intera lista nel file di uscita che diventa una nuova versione del file precedente. Se il file è corto questo procedimento funziona perfettamente ma se il file è molto lungo, diciamo maggiore di 100 Mbyte, finireste per non avere abbastanza memoria per contenere la lista. Fortunatamente esiste il modo "a" che possiamo usare nella funzione open() che ci consente di appendere direttamente ad un file esistente semplicemente con l'istruzione write(). Anzi meglio, se il file non esiste sarà creato proprio come se avessimo usato il modo "W".

Ad esempio supponiamo di avere un file "log" da utilizzare per raccogliere messaggi di errore. Non vogliamo che i messaggi di errore che già contiene siano cancellati, quindi decidiamo di appendere il nuovo messaggio di errore nel modo seguente:

def logErrori(msg):
   err = open("Errori.log","a")
   err.write(msg)
   err.close()

In una vera applicazione probabilmente sarebbe necessario limitare in qualche modo la dimensione del file. Una tecnica molto comune consiste nel creare un nome di file basato sulla data, cosí quando la data cambia automaticamente verrà creato un nuovo file ed in questo modo risulta semplice per l'amministratore del sistema trovare l'errore avvenuto in un dato giorno ed archiviare i file degli errori dei giorni passati se non servono più.

Contiamo le parole

Riprendiamo ora il programma di conteggio delle parole visto in un precedente capitolo. Ricordiamo che lo pseudocodice era il seguente:

def n_parole(s):
    lista = split(s) # lista i cui elementi sono le parole
    return len(lista) # restituisce il numero di elementi della lista

for linea in file:
    totale = totale + n_parole(linea) # somma i totali per ogni linea
print "Il file contiene %d parole" % totale

Adesso che sappiamo come leggere le linee del file consideriamo il corpo della funzione n_parole(). Prima di tutto vogliamo creare una lista contenente tutte le parole di una linea. Se cerchiamo nella documentazione di Python il modulo string troviamo una funzione di nome "split" che divide una stringa in una lista di campi separati da spazi bianchi (o altri caratteri specificati). Cercando ulteriormente nella documentazione troviamo anche la funzione di base len() che riporta il numero di elementi di una lista, che nel nostro caso è il numero di parole nella linea - proprio ciò che vogliamo.

Quindi il codice definitivo è:

import string
def n_parole(s):
    lista = string.split(s) # È necessario specificare il modulo contenete split
    return len(lista) # restituisce il numero di elementi della lista

inp = open("menu.txt","r")
totale = 0  # imposta inzialmente a zero; viene anche creata la variabile

for linea in inp.readlines():
    totale = totale + n_parole(linea) # somma i totali per ogni linea
print "Il file contiene %d parole" % totale

inp.close()

Il programma non è ancora del tutto corretto perché ad esempio conta il carattere '&' come una parola (magari potrebbe essere giusto ...). Inoltre può essere usato solo su un unico file (menu.txt). Però non è poi molto difficile modificarlo in modo che possa leggere il nome del file dalla linea di comando (argv[1] o tramite la funzione raw_input()) come abbiamo gia visto nel capitolo "Conversazione con l'utilizzatore". Lasciamo questa modifica come esercizio per il lettore.

BASIC e Tcl

Anche il BASIC ed il Tcl forniscono i mezzi per trattare i file. Non sono molto diversi dal Python in questo, quindi mostreremo solo il programma 'cat' realizzato con i due linguaggi.

Versione in BASIC

Il BASIC utilizza un concetto detto stream (sequenza, N.d.t.) per indicare i file. Le stream in BASIC sono numerate e questa cosa può rendere macchinoso il loro uso. Per semplificarlo è possibile usare una utile funzione chiamata FREEFILE che riporta il prossimo numero libero usabile come stream. Se lo memorizzate in una variabile non è più necessario dover ricordare quale numero è associato a ciascuna stream o file.

INFILE = FREEFILE
OPEN "TEST.DAT" FOR INPUT AS INFILE
REM Verifica la fine del file (EOF) e poi
REM Leggi una linea dal file e scrivila
DO WHILE NOT EOF(INFILE)
    LINE INPUT #INFILE, Linea
    PRINT Linea
CLOSE #INFILE

Versione in Tcl

Adesso il meccanismo dovrebbe essere chiaro. Ecco la versione in Tcl:

set infile [open "Test.dat" r]
while { [gets $infile linea] >= 0} {
     puts $linea
     }
close $infile
Promemoria
  • I file devono essere aperti prima dell'uso
  • Normalmente i file sono aperti in lettura o in scrittura ma non in entrambi i modi.
  • La funzione readlines() di Python legge tutte le linee di un file mentre readline()
    legge una sola linea alla volta; questo può aiutare a risparmiare memoria.
  • I file devono essere chiusi dopo l'uso.

Precedente  Successivo  Indice


Se avete domande o suggerimenti relativi a questa pagina mandate un e-mail all'autore: alan.gauld@yahoo.co.uk o al traduttore italiano: lfini@arcetri.astro.it