Qual è la controparte perfetta in Python per "while not EOF"


115

Per leggere un file di testo, in C o Pascal, utilizzo sempre i seguenti frammenti per leggere i dati fino all'EOF:

while not eof do begin
  readline(a);
  do_something;
end;

Quindi, mi chiedo come posso farlo in modo semplice e veloce in Python?

Risposte:


192

Fai scorrere il file per leggere le righe:

with open('somefile') as openfileobject:
    for line in openfileobject:
        do_something()

Gli oggetti file sono iterabili e producono righe fino a EOF. L'utilizzo dell'oggetto file come iterabile utilizza un buffer per garantire letture performanti.

Puoi fare lo stesso con lo stdin (non è necessario usare raw_input():

import sys

for line in sys.stdin:
    do_something()

Per completare l'immagine, le letture binarie possono essere eseguite con:

from functools import partial

with open('somefile', 'rb') as openfileobject:
    for chunk in iter(partial(openfileobject.read, 1024), b''):
        do_something()

dove chunkconterrà fino a 1024 byte alla volta dal file e l'iterazione si interrompe quando openfileobject.read(1024)inizia a restituire stringhe di byte vuote.


4
Nota: lineavrà un nuovo carattere di riga alla fine.
ben_joseph

1
Leggere le righe è un po 'pericoloso per i file binari generici, perché forse hai una linea lunga da 6 GiB ...
LtWorf

@ LtWorf: ecco perché mostro come leggere i file binari in blocchi anziché in righe.
Martijn Pieters

Sto leggendo da un stdinprocesso in esecuzione ... quindi non ha mai EOF finché non uccido il processo. Ma poi arrivo alla "fine fino ad ora" e sono in stallo. Come lo rilevo e non il deadlock? Come se non ci fossero nuove righe, smetti di leggere i file (anche se non c'è un EOF, che nel mio caso non esisterà mai).
Charlie Parker

@CharlieParker: se hai raggiunto un deadlock, probabilmente qualcosa si sta dimenticando di svuotare un buffer. Senza un vero MCVE, è difficile dire qualcosa di più.
Martijn Pieters

61

Puoi imitare l'idioma C in Python.

Per leggere un buffer fino al max_sizenumero di byte, puoi fare questo:

with open(filename, 'rb') as f:
    while True:
        buf = f.read(max_size)
        if not buf:
            break
        process(buf)

Oppure un file di testo riga per riga:

# warning -- not idiomatic Python! See below...
with open(filename, 'rb') as f:
    while True:
        line = f.readline()
        if not line:
            break
        process(line)

È necessario utilizzare il while True / breakcostrutto poiché non esiste un test eof in Python oltre alla mancanza di byte restituiti da una lettura.

In C, potresti avere:

while ((ch != '\n') && (ch != EOF)) {
   // read the next ch and add to a buffer
   // ..
}

Tuttavia, non puoi avere questo in Python:

 while (line = f.readline()):
     # syntax error

perché le assegnazioni non sono consentite nelle espressioni in Python (sebbene le versioni recenti di Python possano imitarle utilizzando le espressioni di assegnazione, vedi sotto).

È certamente più idiomatico in Python fare questo:

# THIS IS IDIOMATIC Python. Do this:
with open('somefile') as f:
    for line in f:
        process(line)

Aggiornamento: a partire da Python 3.8 puoi anche usare espressioni di assegnazione :

 while line := f.readline():
     process(line)

@MartijnPieters: Adesso sì :-)
dawg

3
In qualità di programmatore C e Perl, il tuo punto che gli assegnamenti non sono consentiti nelle espressioni è stato cruciale per me.
CODE-REaD

1
Il metodo "while True:" è utile anche quando è necessario operare su più di una riga di input per iterazione, cosa che l'idiomatico Python non consente (per quanto ne so, comunque).
Donald Smith

Non dovresti leggere righe se non fai supposizioni sul file. Un file binario potrebbe avere righe enormi ...
LtWorf

Sembra che ci sia un vantaggio nel modo non idiomatico readline(): puoi gestire gli errori a grana fine, come la cattura UnicodeDecodeError, che non puoi fare con l' foriterazione idiomatica .
flow2k

17

L'idioma Python per aprire un file e leggerlo riga per riga è:

with open('filename') as f:
    for line in f:
        do_something(line)

Il file verrà automaticamente chiuso alla fine del codice precedente (il withcostrutto se ne occupa).

Infine, vale la pena notare che linemanterrà la nuova riga finale. Questo può essere facilmente rimosso utilizzando:

line = line.rstrip()

1
+1, sottolineando anche all'OP che questa non è la stessa cosa molto simile for line in f.readlines(): ..., una soluzione comunemente suggerita.
jedwards

12

Puoi usare lo snippet di codice qui sotto per leggere riga per riga, fino alla fine del file

line = obj.readline()
while(line != ''):

    # Do Something

    line = obj.readline()

1
IMO, questa è l'unica risposta che riflette meglio ciò che è stato chiesto.
gvrocha

Spesso l'iterazione sulle righe distorcerebbe la struttura del programma. Ad esempio, in un parser di linguaggio, si desidera leggere le righe ed elaborarle in sequenza. Non vuoi ristrutturare il livello più alto solo per poter ripetere le linee di lettura e poi inviarle al parser.
Jonathan Starr

11

Sebbene ci siano suggerimenti sopra per "farlo in modo python", se si vuole davvero avere una logica basata su EOF, allora suppongo che usare la gestione delle eccezioni sia il modo per farlo -

try:
    line = raw_input()
    ... whatever needs to be done incase of no EOF ...
except EOFError:
    ... whatever needs to be done incase of EOF ...

Esempio:

$ echo test | python -c "while True: print raw_input()"
test
Traceback (most recent call last):
  File "<string>", line 1, in <module> 
EOFError: EOF when reading a line

Oppure premere Ctrl-Zquando raw_input()richiesto (Windows, Ctrl-ZLinux)


@TessellatingHeckler non è ciò che dice la documentazione : " Generato quando una delle funzioni incorporate (input () o raw_input ()) raggiunge una condizione di fine file (EOF) senza leggere alcun dato."
Tadhg McDonald-Jensen

1
@ TadhgMcDonald-Jensen Ehi, così sarà. Che strano. Falso reclamo ritirato e downvote ingiusto rimosso.
TessellatingHeckler

1

Puoi utilizzare il seguente frammento di codice. readlines () legge l'intero file in una volta e lo divide per riga.

line = obj.readlines()

0

Oltre all'ottima risposta di @ dawg, la soluzione equivalente che utilizza l'operatore walrus (Python> = 3.8):

with open(filename, 'rb') as f:
    while buf := f.read(max_size):
        process(buf)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.