lunedì 26 novembre 2012

Mission Python: I file, leggere e scrivere

Oggi proviamo una cosa nuova: leggere e scrivere file. Intanto, seguendo un'antica e ottima tradizione di Unix/Linux tratteremo solo file di testo, quelli visualizzabili con cat o less. Conoscete tutti cat e less, vero?

Per cominciare, visto che un file da leggere non ce l'abbiamo lo creiamo, così vediamo come si usa write (scrivi.py):

#!/usr/bin/python
# -*- coding: utf-8 -*-

#apro il file
nome = "testo.txt"
fw = open(nome, "w")

#scrivo
fw.write('prima riga di' + nome + '\n')
fw.write('seconda riga\nterza riga\n')
fw.write('dopo questa una riga vuota\n')
fw.write('\n')
fw.write('si possono mettere anche variabli\n')
a = 1
b = 4.2
c = 2 ** 0.5
fw.write(str(a) + ' ' + str(b) + ' ' + str(c) + '\n')
fw.write("adesso chiudo il flie\n")
fw.close()

Allora:
open() ha come parametri il nome del file, che per semplicità creiamo nella directory corrente (vedremo in una prossima puntata come gestire i percorsi, promesso) e la modalità, in questo caso w per write. Restituisce il descrittore del file, che sarà usato per tutte le operazioni successive che riguardano il file. Con la modalità w il file se non esiste viene creato, se esiste viene aperto cancellando il suo contenuto.
write() accetta come parametro una sola stringa. Per cui se vogliamo scrivere dei numeri dobbiamo prima trasformarli in stringhe con str(). Se abbiamo diverse stringhe da scrivere dobbiamo concatenarle con +.
Naturalmente in seguito vedremo come superare queste limitazioni con format() che essendo una brutta bestia richiederà un post tutto suo (e tanta pazienza).
Una particolarità di write() è che non va mai a capo, dovete dirglielo voi con la sequenza di escape \n (OK, ve lo dimenticherete spesso, anch'io).

close() chiude il file; da adesso in poi il decrittore fw non ha più valore.

Ma, aspetta un attimino: mi accorgo adesso di aver scritto "flie" al posto di "file" nell'ultima riga. Si potrebbe correggere lo script ma sarebbe troppo facile, e poi non potrei farvi vedere la modalità a, append (aggiungi.py).
#!/usr/bin/python
# -*- coding: utf-8 -*-

#apro il file
nome = "testo.txt"
fw = open(nome, "a")

#scrivo
fw.write('cioè volevo dire')
fw.write(' file\n')
fw.write("adesso ri-chiudo il file\n")
fw.close()

OK, adesso che un file ce l'abbiamo, vediamo come si fa a leggerlo. Lasciando perdere cose speciali (che potrete vedere sull'help online quando vi serviranno) abbiamo due modi: 1) una riga per volta; o 2) tutto in un colpo.

Cominciamo a vedere come leggere una riga per volta (leggiriga.py)
#!/usr/bin/python
# -*- coding: utf-8 -*-

#apro il file
nome = "testo.txt"
fw = open(nome, "r")

# leggo fino alla fine del file una riga
# per volta e la scrivo sul terminale
riga = fw.readline()
while len(riga) > 0:
 riga_ok = riga.rstrip('\n')
 print riga_ok
 riga = fw.readline()
fw.close()

Per leggerlo il file dev'essere aperto con modalità r = read.
Lo leggiamo con read_line().
L'a-capo resta attaccato alla stringa ma, niente panico, lo togliamo con rstrip(). Se usassimo strip() toglierebbe tutti gli spazi e tab iniziali e finali oltre all'a-capo. Per cui usiamo rstrip() r come right, destra e gli passiamo il (o i) caratteri che vogliamo strippare.
Finché non viene raggiunta la fine del file readline() ritorna una stringa non nulla (al limite con solo l'a-capo per la riga vuota) per cui possiamo verificare abbiamo finito con len().

Adesso vediamo di leggere il file tutto in una volta sola, funziona per file non troppo grossi (qualche decina di MB, il fatto è che poi dovete gestire il malloppo). Questo metodo è più efficiente in quanto si limitano gli accessi al disco (o quello che è), più lento delle operazioni in memoria (leggitutto.py).
#!/usr/bin/python
# -*- coding: utf-8 -*-

#apro il file
nome = "testo.txt"
fw = open(nome, "r")

# leggo fino alla fine del file una riga
# per volta e la scrivo sul terminale
text = fw.read()
fw.close()

print "type(text) =", type(text), '\n'
print text

OK, vediamo che funziona, mette tutto in una stringa (lo vediamo con type()). Ma probabilmente adesso avremo un problema se dobbiamo trattare riga per riga. OK, se c'è un problema ci sarà anche la soluzione (dividi.py):
#!/usr/bin/python
# -*- coding: utf-8 -*-

#apro il file
nome = "testo.txt"
fw = open(nome, "r")

# leggo fino alla fine del file una riga
# per volta e la scrivo sul terminale
text = fw.read()
fw.close()

righe = text.split('\n')
print "type(text) =", type(text)
print "type(righe) =", type(righe)

# scrivo su terminale le stringhe della lista righe

n = 0
for st in righe:
 n += 1
 print n, " - ", st

Usiamo la split() per separare le varie stringhe. C'è un piccolo problema: abbiamo una stringa vuota in fondo alla lista. La soluzione è semplice (la prova è lasciata come esercizio): dopo la split() inserire l'istruzione
righe.pop()

I più volonterosi potrebbero anche approfondire la pop() e sostituire il ciclo for.

Un'ultima cosa, guardate qui (manca.py)
#!/usr/bin/python
# -*- coding: utf-8 -*-

#apro il file
nome = "manca.txt"
fw = open(nome, "r")

fw.close()

OPS! il file manca.txt non esiste e lo script va in errore.
Al solito, niente panico, ecco (esiste.txt):
#!/usr/bin/python
# -*- coding: utf-8 -*-

import os.path

manca = "manca.txt"
questo_ce = "testo.txt"
print os.path.isfile(manca)
print os.path.isfile(questo_ce)

Ecco. Proprio quello che serve. Per oggi credo basti, vero?
Ah! no, una nota: nei nomi dei file di testo ho usato l'estensione ".txt" come fanno quelli di Windoze; naturalmente da noi non è obbligatoria.

Juhan

Vi ricordo che l'indice di Mission Python lo trovate qui.

Alla prossima!

Nessun commento:

Posta un commento