为什么我不能在打开的文件上两次调用read()函数?

127

我正在进行练习,尝试使用read()方法两次读取给定文件的内容。奇怪的是,第二次调用它时,它似乎没有将文件内容作为字符串返回。

以下是代码:

f = f.open()

# get the year
match = re.search(r'Popularity in (\d+)', f.read())

if match:
  print match.group(1)

# get all the names
matches = re.findall(r'<td>(\d+)</td><td>(\w+)</td><td>(\w+)</td>', f.read())

if matches:
  # matches is always None

当然我知道这不是最有效或最好的方法,但这不是重点。问题是为什么我不能调用read()两次?我必须重置文件句柄吗?还是必须关闭/重新打开文件才能这样做?


2
你从哪里得到的读取文件不会改变文件状态的想法?你使用了什么参考或教程? - S.Lott
1
@Shynthriir:关闭和重新打开文件并不总是一个好主意,因为它可能会对系统产生其他影响(临时文件、incron 等)。 - Ignacio Vazquez-Abrams
4
我想要明确一件事:你确实调用了两次read()! - unbeknown
6
关于S.Lott,从5年前开始:这确实需要在Python文档中说明。如果一个人习惯于使用不可变数据/函数式编程,那么很明显他们不会假设读取文件对象会改变任何状态。 - Paul Gowder
1
@PaulGowder不同意-因为这是由于文件的基本性质引起的,在每种编程语言中都是相同的。当然,从文件中读取会改变文件对象的状态-因为否则它怎么知道读取了什么,以及从哪里开始下一次读取?当然,“读取文件的其余部分;然后再读取文件的其余部分”第二次会得到一个空结果,出于同样的原因,“从文件中读取一行;然后再从文件中读取一行”每次都会得到不同的行。没有这个,你怎么能迭代整个文件呢? - Karl Knechtel
显示剩余3条评论
7个回答

197

调用 read() 会读取整个文件,并且将读取光标放置在文件末尾(没有更多内容可读)。如果你想每次读取特定行数,可以使用 readline(), readlines() 或者通过 for line in handle: 迭代读取每一行。

直接回答您的问题,当一个文件使用 read() 读取完毕后,您可以使用 seek(0) 将读取光标移动到文件开头(文档在这里)。如果您知道文件不会太大,您也可以将 read() 的输出保存到变量中,并在 findall 表达式中使用它。

附:完成文件操作后,请别忘了关闭文件。


4
+1,请将内容读取到临时变量中,以避免不必要的文件I/O操作。因为你使用更少的(显式)变量并不能节省内存,这是一种虚假的经济效益。 - Nick T
3
@NickT:我认为在Linux/OSX系统中,多次读取的小文件会被操作系统缓存,因此不需要额外的文件I/O来读取两次。无法放入内存的大文件不会被缓存,但是你不想将它们读入变量,因为那样会导致开始交换。所以当有疑问时,总是多次读取。如果你确定文件很小,就按照最好的程序进行操作。 - Claude
5
拆除可以使用with自动化。 - Cees Timmerman

50

正如其他答案建议的那样,您应该使用 seek()

我只会写一个例子:

>>> a = open('file.txt')
>>> a.read()
#output
>>> a.seek(0)
>>> a.read()
#same output

22

到目前为止回答这个问题的每个人都是完全正确的——read()会遍历文件,因此在调用它后,您无法再次调用它。

我要补充的是,在您的特定情况下,您不需要将指针回溯到开头或重新打开文件,而是可以将已读取的文本存储在本地变量中,并在程序中使用它两次或多次:

f = f.open()
text = f.read() # read the file into a local variable
# get the year
match = re.search(r'Popularity in (\d+)', text)
if match:
  print match.group(1)
# get all the names
matches = re.findall(r'<td>(\d+)</td><td>(\w+)</td><td>(\w+)</td>', text)
if matches:
  # matches will now not always be None

1
+1 实际上,这是这个练习的建议解决方案(http://code.google.com/intl/de-DE/edu/languages/google-python-class/exercises/baby-names.html)。但不知何故,我没有想到将字符串存储在变量中。唉! - helpermethod
1
使用Python3,使用pathlib。from pathlib import Path; text = Path(filename).read_text() 它会处理打开、关闭等操作。 - PaulMcG

15
读取指针移动到最后一个读取的字节/字符之后。使用seek()方法将读取指针倒回到开头。

3
每个打开的文件都有一个关联的位置。
当你读取时,从该位置开始读取。比如read(10)从一个新打开的文件中读取前10个字节,然后另一个read(10)读取接下来的10个字节。read()没有参数时读取文件的全部内容,并将文件位置设置为文件末尾。下次调用read()时就没有可读取的内容了。
你可以使用seek移动文件位置。或者更好的方式是进行一次read()并保留结果供两次搜索使用。

1

read() 会消耗文件。因此,在重新读取之前,您可以重置文件或定位到开头。或者,如果适合您的任务,您可以使用read(n)仅消耗n个字节。


-1
我总觉得read方法有点像走在一个黑暗的小巷里。你往下走一点,然后停下来,但如果你不数步数,你就不确定自己走了多远。seek通过重新定位给出了解决方案,另一个选择是Tell,它返回文件中的位置。也许Python文件API可以将read和seek合并为read_from(position,bytes),以使操作更简单 - 在此之前,您应该阅读this page

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接