在Python中,“虽然不是EOF”的完美替代品是什么?


114

要读取一些文本文件,无论是C还是Pascal,我始终使用以下代码段读取数据,直到EOF:

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

因此,我想知道如何在Python中简单快速地做到这一点?

Answers:


189

循环遍历文件以读取行:

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

文件对象是可迭代的,并在EOF之前产生行。将文件对象用作可迭代对象使用缓冲区来确保性能读取。

您可以使用stdin进行相同操作(无需使用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
读取行对于通用二进制文件来说有点危险,因为也许您有6GiB长行…
LtWorf

@LtWorf:这就是为什么我显示如何以而不是行读取二进制文件的原因。
马丁·彼得斯

我正在从stdin正在运行的进程中读取信息...因此,直到我终止该进程,它才不会出现EOF。但是后来我到达了“到现在为止”,陷入僵局。我如何检测到这一点而不是死锁?就像没有新行一样,请停止读取文件(即使没有EOF,在我看来,它也不存在)。
查理·帕克

@CharlieParker:如果您遇到了死锁,则可能是有些事情忘记了刷新缓冲区。没有实际的MCVE,很难说的比这更多。
马丁·彼得斯

61

您可以在Python中模仿C语言。

要读取不超过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)

@MartijnPieters:现在可以了:-)
dawg

3
作为C和Perl程序员,您的观点是对表达式不允许赋值对我来说至关重要。
CODE-READ

1
当每次迭代需要在多个输入行上进行操作时,“ while True:”方法也很有用,这是惯用的Python不允许的(据我所知,无论如何)。
唐纳德·史密斯,

如果不对文件进行假设,则不应阅读行。二进制文件可能有很多行……
LtWorf

似乎非惯用readline()方式有一个优点:您可以执行细粒度的错误处理,例如catch UnicodeDecodeError,而惯用for迭代则无法做到。
flow2k

17

用于打开文件并逐行读取的Python习惯用法是:

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

该文件将在上述代码的末尾自动关闭(该with结构将完成此工作)。

最后,值得注意的是line将保留尾随的换行符。可以使用以下方法轻松删除它:

line = line.rstrip()

1
+1,同时向OP指出这与通常建议的非常相似的解决方案并不相同for line in f.readlines(): ...
jedwards

12

您可以使用下面的代码片段逐行读取,直到文件结尾

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

    # Do Something

    line = obj.readline()

1
海事组织,这是最能反映所问问题的一个答案。
gvrocha '17

通常,在各行上进行迭代会扭曲程序的结构。例如,在语言解析器中,您想要读取行并按顺序处理它们。您不希望仅重组顶层结构,而是可以循环读取行,然后将其发送到解析器。
乔纳森·斯塔尔

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-Zraw_input()提示符(Windows,Ctrl-ZLinux的)


@TessellatingHeckler并不是文档所说的:“当内置函数之一(input()或raw_input())达到文件结尾条件(EOF)而未读取任何数据时引发。”
塔德格·麦当劳-詹森

1
@ TadhgMcDonald-Jensen好吧,所以会的。真奇怪 虚假主张已撤回,不正当的反对意见已删除。
TessellatingHeckler


0

除了@dawg的好答案之外,使用walrus运算符的等效解决方案(Python> = 3.8):

with open(filename, 'rb') as f:
    while buf := f.read(max_size):
        process(buf)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.