Какой идеальный аналог в Python для «пока не EOF»

115

Чтобы прочитать текстовый файл на C или Pascal, я всегда использую следующие фрагменты для чтения данных до EOF:

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

Поэтому мне интересно, как я могу сделать это просто и быстро на Python?

Аллен Ку
источник

Ответы:

192

Прокрутите файл, чтобы прочитать строки:

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

Файловые объекты являются итерируемыми и выдают строки до EOF. При использовании файлового объекта в качестве итерируемого используется буфер для обеспечения выполнения операций чтения.

Вы можете сделать то же самое со стандартным вводом (не нужно использовать raw_input():

import sys

for line in sys.stdin:
    do_something()

Чтобы завершить картину, двоичное чтение может быть выполнено с помощью:

from functools import partial

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

где chunkбудет содержать до 1024 байтов из файла за раз, и итерация останавливается, когда openfileobject.read(1024)начинает возвращать пустые байтовые строки.

Мартейн Питерс
источник
4
Примечание: lineв конце будет символ новой строки.
ben_joseph
1
Чтение строк немного опасно для обычных двоичных файлов, потому что, возможно, у вас длинная строка в 6 ГБ ...
LtWorf
@LtWorf: вот почему я показываю, как читать двоичные файлы кусками, а не строками.
Мартейн Питерс
Я читаю stdinиз запущенного процесса ... поэтому у него никогда не будет EOF, пока я не убью процесс. Но потом я дохожу до «до настоящего момента» и захожу в тупик. Как мне обнаружить это, а не тупик? Например, если нет новых строк, прекратите читать файлы (даже если нет EOF, которого в моем случае никогда не будет).
Чарли Паркер
@CharlieParker: если вы зашли в тупик, значит, что-то, вероятно, забывает очистить буфер. Без реального MCVE трудно сказать что-либо большее, чем это.
Мартейн Питерс
61

Вы можете имитировать идиому C в Python.

Чтобы прочитать буфер до max_sizeколичества байтов, вы можете сделать это:

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

Или текстовый файл построчно:

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

Вам нужно использовать while True / breakконструкцию, поскольку в Python нет теста eof, кроме отсутствия байтов, возвращаемых при чтении.

В C у вас может быть:

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

Однако в Python этого не может быть:

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

потому что присваивания не разрешены в выражениях в Python (хотя последние версии Python могут имитировать это с помощью выражений присваивания, см. ниже).

В Python, безусловно, более идиоматично делать это:

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

Обновление: начиная с Python 3.8 вы также можете использовать выражения присваивания :

 while line := f.readline():
     process(line)
Dawg
источник
@MartijnPieters: Теперь это так :-)
dawg
3
Как программист на C и Perl ваше мнение о том, что присваивания в выражениях недопустимы, было для меня решающим.
CODE-REaD
1
Метод «while True:» также полезен, когда вам нужно работать с более чем одной входной строкой на итерацию, чего не позволяет идиоматический Python (во всяком случае, насколько я могу судить).
Дональд Смит
Вам не следует читать строки, если вы не делаете предположений относительно файла. В двоичном файле могут быть огромные строки…
LtWorf
Похоже, что у неидиоматического readline()способа есть преимущество : вы можете выполнять мелкую обработку ошибок, например отлов UnicodeDecodeError, чего нельзя сделать с идиоматической forитерацией.
flow2k
17

Идиома Python для открытия файла и чтения его построчно:

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

Файл будет автоматически закрыт в конце приведенного выше кода ( withконструкция позаботится об этом).

Наконец, стоит отметить, что lineзавершающий символ новой строки будет сохранен. Это легко удалить с помощью:

line = line.rstrip()
NPE
источник
1
+1, также указывая OP, что это не то же самое, что очень похожее for line in f.readlines(): ..., обычно предлагаемое решение.
jedwards
12

Вы можете использовать приведенный ниже фрагмент кода для чтения построчно до конца файла.

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

    # Do Something

    line = obj.readline()
Арканзас
источник
1
ИМО, это единственный ответ, который лучше всего отражает то, о чем спрашивали.
gvrocha
Часто повторение строк искажало бы структуру программы. Например, в синтаксическом анализаторе языка вы хотите прочитать строки и обработать их последовательно. Вы не хотите реструктурировать верхний уровень только для того, чтобы вы могли зациклить строки чтения и затем отправить их синтаксическому анализатору.
Джонатан Старр
11

Несмотря на то, что выше есть предложения «сделать это способом Python», если кто-то действительно хочет иметь логику, основанную на EOF, то я полагаю, что использование обработки исключений - способ сделать это -

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

Пример:

$ 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

Или нажмите Ctrl-Zв raw_input()командной строке (Windows, Ctrl-ZLinux)

user5472996
источник
@TessellatingHeckler - это не то, о чем говорится в документации : «Возникает, когда одна из встроенных функций (input () или raw_input ()) достигает состояния конца файла (EOF) без чтения каких-либо данных».
Tadhg McDonald-Jensen
1
@ TadhgMcDonald-Jensen Что ж, так и будет. Как странно. Ложная заявка отозвана, а несправедливый голос против нее удален.
TessellatingHeckler
1

Вы можете использовать следующий фрагмент кода. readlines () читает сразу весь файл и разбивает его по строкам.

line = obj.readlines()
Адитея Пандей
источник
0

В дополнение к отличному ответу @dawg, эквивалентное решение с использованием оператора моржа (Python> = 3.8):

with open(filename, 'rb') as f:
    while buf := f.read(max_size):
        process(buf)
бесконечность
источник