- Introduzione
- Lettura di base dei file in Python
- Lettura riga per riga
- Un’applicazione di esempio
- Conclusione
Introduzione
Nel corso della mia vita lavorativa ho avuto l’opportunità di usare molti concetti e tecnologie di programmazione per fare innumerevoli cose. Alcune di queste cose coinvolgono frutti di valore relativamente basso del mio lavoro, come l’automatizzazione dell’errore incline o mondano come la generazione di rapporti, l’automazione dei compiti e la riformattazione generale dei dati. Altre sono state molto più preziose, come lo sviluppo di prodotti di dati, applicazioni web e pipeline di analisi ed elaborazione dei dati. Una cosa che è notevole in quasi tutti questi progetti è la necessità di aprire semplicemente un file, analizzare il suo contenuto, e fare qualcosa con esso.
Tuttavia, cosa si fa quando il file che si sta cercando di consumare è abbastanza grande? Cosa succede se il file è di diversi GB di dati o più grande? Di nuovo, questo è stato un altro aspetto frequente della mia carriera di programmatore, che è stata trascorsa principalmente nel settore BioTech, dove è comune incontrare file di dimensioni superiori a 1 TB.
La risposta a questo problema è quella di leggere pezzi di un file alla volta, elaborarlo, quindi liberarlo dalla memoria in modo da poter estrarre ed elaborare un altro pezzo fino a quando l’intero file enorme è stato elaborato. Mentre sta al programmatore determinare una dimensione adatta dei pezzi, per molte applicazioni è adatto elaborare un file una riga alla volta.
In questo articolo copriremo una serie di esempi di codice per mostrare come leggere i file riga per riga. Nel caso vogliate provare da soli alcuni di questi esempi, il codice usato in questo articolo può essere trovato nel seguente repo GitHub.
Basic File IO in Python
Essendo un grande linguaggio di programmazione generale, Python ha una serie di funzionalità molto utili per l’IO dei file nella sua libreria standard di funzioni e moduli integrati. La funzione integrata open()
è quella che si usa per aprire un oggetto file sia in lettura che in scrittura.
fp = open('path/to/file.txt', 'r')
La funzione open()
accetta più argomenti. Ci concentreremo sui primi due, con il primo che è un parametro stringa posizionale che rappresenta il percorso del file che dovrebbe essere aperto. Il secondo parametro opzionale è anch’esso una stringa, che specifica la modalità di interazione che si intende per l’oggetto file restituito dalla chiamata alla funzione. Le modalità più comuni sono elencate nella tabella sottostante, con il valore predefinito che è ‘r’ per la lettura.
Modalità | Descrizione |
---|---|
r |
Apri per leggere testo semplice |
w |
Apri per scrivere testo semplice |
a |
Apri un file esistente per aggiungere testo |
rb |
Apri per la lettura di dati binari |
wb |
Apri per la scrittura di dati binari |
Una volta che avete scritto o letto tutti i dati desiderati per un oggetto file è necessario chiudere il file in modo che le risorse possano essere riassegnate sul sistema operativo su cui il codice è in esecuzione.
fp.close()
Vedrete spesso molti frammenti di codice su internet o in programmi in libertà che non chiudono esplicitamente gli oggetti file che sono stati generati secondo l’esempio precedente. È sempre una buona pratica chiudere una risorsa oggetto file, ma molti di noi o sono troppo pigri o dimentichi per farlo o pensano di essere intelligenti perché la documentazione suggerisce che un oggetto file aperto si chiuderà da solo una volta che un processo termina. Questo non è sempre il caso.
Invece di insistere su quanto sia importante chiamare sempre close()
su un oggetto file, vorrei fornire un modo alternativo e più elegante per aprire un oggetto file e assicurare che l’interprete Python pulisca dopo di noi:)
with open('path/to/file.txt') as fp: # do stuff with fp
Utilizzando semplicemente la parola chiave with
(introdotta in Python 2.5) per avvolgere il nostro codice per l’apertura di un oggetto file, gli interni di Python faranno qualcosa di simile al seguente codice per assicurare che non importa cosa l’oggetto file venga chiuso dopo l’uso.
try: fp = open('path/to/file.txt') # do stuff with fpfinally: fp.close()
Ognuno di questi due metodi è adatto, con il primo esempio che è il modo più “pitonico”.
Lettura riga per riga
Ora, passiamo alla lettura effettiva di un file. L’oggetto file restituito da open()
ha tre metodi espliciti comuni (read
readline
, e readlines
) per leggere i dati e un altro modo implicito.
Il metodo read
leggerà tutti i dati in una stringa di testo. Questo è utile per i file più piccoli dove vorreste fare una manipolazione del testo sull’intero file, o qualsiasi altra cosa vi convenga. Poi c’è readline
che è un modo utile per leggere solo quantità incrementali di linee individuali alla volta e restituirle come stringhe. L’ultimo metodo esplicito, readlines
, leggerà tutte le linee di un file e le restituirà come una lista di stringhe.
Come detto prima, potete usare questi metodi per caricare solo piccoli pezzi di file alla volta. Per farlo con questi metodi, potete passare loro un parametro che dica quanti byte caricare alla volta. Questo è l’unico argomento che questi metodi accettano.
Un’implementazione per leggere un file di testo una riga alla volta è mostrata qui sotto, che è fatta tramite il metodo readline()
.
Nota: Per il resto di questo articolo dimostrerò come leggere il testo del libro “Iliade di Omero”, che può essere trovato su gutenberg.org, così come nel repo GitHub dove si trova il codice per questo articolo.
In readline.py troverete il seguente codice. Nel terminale se esegui $ python readline.py
puoi vedere l’output della lettura di tutte le righe dell’Iliade, così come i loro numeri di riga.
Il codice di cui sopra apre un oggetto file memorizzato come una variabile chiamata fp
, poi legge una riga alla volta chiamando readline
su quell’oggetto file iterativamente in un while
ciclo e lo stampa sulla console.
Eseguendo questo codice si dovrebbe vedere qualcosa di simile al seguente:
$ python forlinein.py Line 0: BOOK ILine 1: Line 2: The quarrel between Agamemnon and Achilles--Achilles withdrawsLine 3: from the war, and sends his mother Thetis to ask Jove to helpLine 4: the Trojans--Scene between Jove and Juno on Olympus.Line 5: Line 6: Sing, O goddess, the anger of Achilles son of Peleus, that broughtLine 7: countless ills upon the Achaeans. Many a brave soul did it sendLine 8: hurrying down to Hades, and many a hero did it yield a prey to dogs andLine 9: vultures, for so were the counsels of Jove fulfilled from the day on...
Mentre questo va perfettamente bene, c’è un modo finale che ho menzionato fugacemente prima, che è meno esplicito ma un po’ più elegante, che preferisco di gran lunga. Questo modo finale di leggere un file riga per riga include l’iterazione su un oggetto file in un ciclo for
, assegnando ogni riga a una variabile speciale chiamata line
. Lo snippet di codice di cui sopra può essere replicato nel seguente codice, che può essere trovato nello script Python forlinein.py:
filepath = 'Iliad.txt'with open(filepath) as fp: for cnt, line in enumerate(fp): print("Line {}: {}".format(cnt, line))
In questa implementazione stiamo approfittando di una funzionalità integrata in Python che ci permette di iterare sull’oggetto file implicitamente usando un for
ciclo in combinazione con l’oggetto iterable fp
. Non solo questo è più semplice da leggere, ma richiede anche meno righe di codice da scrivere, che è sempre una best practice degna di essere seguita.
Un’applicazione di esempio
Sarei negligente a scrivere un’applicazione su come consumare informazioni in un file di testo senza dimostrare almeno un uso banale di come utilizzare tale abilità. Detto questo, dimostrerò una piccola applicazione che può essere trovata in wordcount.py, che calcola la frequenza di ogni parola presente in “L’Iliade di Omero” usata negli esempi precedenti. Questo crea un semplice bag of words, che è comunemente usato nelle applicazioni NLP.
Il codice qui sopra rappresenta uno script python a riga di comando che si aspetta un percorso del file passato come argomento. Lo script usa il modulo os
per assicurarsi che il percorso del file passato sia un file esistente sul disco. Se il percorso esiste, allora ogni linea del file viene letta e passata a una funzione chiamata record_word_cnt
come una lista di stringhe, delimitate dagli spazi tra le parole e da un dizionario chiamato bag_of_words
. La funzione record_word_cnt
conta ogni istanza di ogni parola e la registra nel dizionario bag_of_words
.
Una volta che tutte le righe del file sono state lette e registrate nel dizionario bag_of_words
, viene chiamata un’ultima funzione order_bag_of_words
, che restituisce una lista di tuple in formato (parola, numero di parole), ordinate per numero di parole. La lista di tuple restituita è usata per stampare le 10 parole più frequenti.
Conclusione
Così, in questo articolo abbiamo esplorato i modi per leggere un file di testo riga per riga in due modi, incluso un modo che mi sembra un po’ più pitonico (questo è il secondo modo dimostrato in forlinein.py). Per concludere ho presentato una banale applicazione che è potenzialmente utile per leggere e pre-elaborare i dati che potrebbero essere utilizzati per l’analisi del testo o la sentiment analysis.
0 commenti