Qual é a contraparte perfeita em Python para "while not EOF"

114

Para ler algum arquivo de texto, em C ou Pascal, sempre uso os seguintes trechos para ler os dados até a EOF:

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

Assim, eu me pergunto como posso fazer isso de forma simples e rápida em Python?

Allen Koo
fonte

Respostas:

189

Faça um loop no arquivo para ler as linhas:

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

Os objetos de arquivo são iteráveis ​​e geram linhas até EOF. Usar o objeto de arquivo como um iterável usa um buffer para garantir leituras de desempenho.

Você pode fazer o mesmo com o stdin (não há necessidade de usar raw_input():

import sys

for line in sys.stdin:
    do_something()

Para completar a imagem, as leituras binárias podem ser feitas com:

from functools import partial

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

onde chunkconterá até 1024 bytes por vez do arquivo, e a iteração para quando openfileobject.read(1024)começa a retornar cadeias de bytes vazias.

Martijn Pieters
fonte
4
Nota: O lineterá um novo caractere de linha no final.
ben_joseph
1
Ler linhas é um pouco perigoso para arquivos binários genéricos, porque talvez você tenha uma longa linha de 6
GiB
@LtWorf: é por isso que mostro como ler arquivos binários em blocos em vez de linhas.
Martijn Pieters
Estou lendo de um stdinprocesso em execução ... portanto, nunca terá EOF até que eu mate o processo. Mas então eu chego ao "fim até agora" e entro em um impasse. Como faço para detectar isso e não um deadlock? Como se não houvesse novas linhas, pare de ler os arquivos (mesmo que não exista um EOF, que no meu caso nunca existirá).
Charlie Parker
@CharlieParker: se você atingiu um deadlock, provavelmente algo está esquecendo de liberar um buffer. Sem um MCVE real, é difícil dizer mais do que isso.
Martijn Pieters
61

Você pode imitar o idioma C em Python.

Para ler um buffer de até um max_sizenúmero de bytes, você pode fazer o seguinte:

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

Ou um arquivo de texto linha por linha:

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

Você precisa usar while True / breakconstruct, uma vez que não há teste eof em Python além da falta de bytes retornados de uma leitura.

Em C, você pode ter:

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

No entanto, você não pode ter isso em Python:

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

porque atribuições não são permitidas em expressões em Python (embora versões recentes de Python possam simular isso usando expressões de atribuição, veja abaixo).

Certamente é mais idiomático em Python fazer isso:

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

Atualização: desde o Python 3.8, você também pode usar expressões de atribuição :

 while line := f.readline():
     process(line)
dawg
fonte
@MartijnPieters: Agora sim :-)
dawg
3
Como um programador C e Perl, seu ponto de que atribuições não são permitidas em expressões foi crucial para mim.
CODE-REaD
1
O método "while True:" também é útil quando você precisa operar em mais de uma linha de entrada por iteração, algo que o Python idiomático não permite (pelo menos pelo que posso dizer).
Donald Smith
Você não deve ler linhas se não fizer suposições sobre o arquivo. Um arquivo binário pode ter linhas enormes ...
LtWorf,
Parece que há uma vantagem na maneira não idiomática readline(): você pode fazer um tratamento de erros refinado, como a captura UnicodeDecodeError, o que não pode ser feito com a foriteração idiomática .
flow2k
17

O idioma Python para abrir um arquivo e lê-lo linha por linha é:

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

O arquivo será fechado automaticamente no final do código acima (a withconstrução cuida disso).

Finalmente, é importante notar que linepreservará a nova linha final. Isso pode ser facilmente removido usando:

line = line.rstrip()
NPE
fonte
1
+1, apontando também para o OP que este não é o mesmo que muito semelhante for line in f.readlines(): ..., solução comumente sugerida.
jedwards
12

Você pode usar o trecho de código abaixo para ler linha por linha, até o final do arquivo

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

    # Do Something

    line = obj.readline()
AR
fonte
1
IMO, esta é a resposta que melhor reflete o que foi perguntado.
gvrocha
Freqüentemente, a iteração nas linhas distorce a estrutura do programa. Por exemplo, em um analisador de linguagem, você deseja ler as linhas e processá-las em sequência. Você não quer reestruturar o nível superior apenas para poder ler as linhas em loop e enviá-las ao analisador.
Jonathan Starr
11

Embora existam sugestões acima para "fazer do jeito python", se alguém realmente quiser ter uma lógica baseada em EOF, então suponho que usar o tratamento de exceções é a maneira de fazer isso -

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

Exemplo:

$ 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

Ou pressione Ctrl-Zem um raw_input()prompt (Windows, Ctrl-ZLinux)

user5472996
fonte
@TessellatingHeckler não é o que a documentação diz: "Gerado quando uma das funções internas (input () ou raw_input ()) atinge uma condição de fim de arquivo (EOF) sem ler nenhum dado."
Tadhg McDonald-Jensen
1
@ TadhgMcDonald-Jensen Bem, ei, então vai. Que estranho. Alegação falsa retirada e voto negativo injusto removido.
TessellatingHeckler
1

Você pode usar o seguinte trecho de código. readlines () lê todo o arquivo de uma vez e o divide por linha.

line = obj.readlines()
Aditeya Pandey
fonte
0

Além da ótima resposta de @dawg, a solução equivalente usando o operador walrus (Python> = 3.8):

with open(filename, 'rb') as f:
    while buf := f.read(max_size):
        process(buf)
Infinidade
fonte