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?

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.


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)

@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()

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()

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)


@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()

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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.