如何读取一个非常大的文本文件的最后一个MB

时间:2013-09-27 08:39:13

标签: python file text jython

我正在尝试在文本文件末尾附近找到一个字符串。问题是文本文件的大小可能有很大差异。从3MB到4GB。但每次我尝试运行脚本在大约3GB的文本文件中找到此字符串时,我的计算机内存不足。所以我想知道是否有任何方式让python找到文件的大小,然后读取它的最后一兆字节。

我目前使用的代码如下,但正如我之前所说,我似乎没有足够大的内存来读取这么大的文件。

find_str = "ERROR"
file = open(file_directory)                           
last_few_lines​ = file.readlines()[-20:]   

error​ = False  

for line in ​last_few_lines​:
    if find_str in line:
    ​    error​ = True

3 个答案:

答案 0 :(得分:26)

使用file.seek()

import os
find_str = "ERROR"
error = False
# Open file with 'b' to specify binary mode
with open(file_directory, 'rb') as file:
    file.seek(-1024 * 1024, os.SEEK_END)  # Note minus sign
    if find_str in file.read():
        error = True

您必须在打开文件时指定二进制模式,否则您将获得“未定义的行为”。在python2下,无论如何它都可以工作(它对我来说),但是在python3下seek()如果在默认文本模式下打开文件,则会引发io.UnsupportedOperation异常。 python 3文档是here。虽然从这些文档中不清楚,但SEEK_*常量仍在os模块中。

更新:根据Chris Betti的建议,使用with statement进行更安全的资源管理。

答案 1 :(得分:2)

您可以使用tail recipedeque来获取大文件的最后n行:

from collections import deque

def tail(fn, n):
    with open(fn) as fin:
        return list(deque(fin, n))

现在测试一下。

首先创建一个大文件:

>>> with open('/tmp/lines.txt', 'w') as f:
...    for i in range(1,10000000+1):
...       print >> f, 'Line {}'.format(i)  # Python 3: print('Line {}'.format(i), file=f)

# about 128 MB on my machine

然后测试:

print tail('/tmp/lines.txt', 20) 
# ['Line 9999981\n', 'Line 9999982\n', 'Line 9999983\n', 'Line 9999984\n', 'Line 9999985\n', 'Line 9999986\n', 'Line 9999987\n', 'Line 9999988\n', 'Line 9999989\n', 'Line 9999990\n', 'Line 9999991\n', 'Line 9999992\n', 'Line 9999993\n', 'Line 9999994\n', 'Line 9999995\n', 'Line 9999996\n', 'Line 9999997\n', 'Line 9999998\n', 'Line 9999999\n', 'Line 10000000\n']

这将返回最后n行,而不是文件的最后X个字节。数据的大小与行的大小相同 - 而不是文件的大小。 file object fin用作文件行的迭代器,因此整个文件不会同时驻留在内存中。

答案 2 :(得分:1)

使用seek的建议答案是对您的问题的正确答案,但我认为这不是您真正想要做的。您的解决方案将整个文件加载到内存中,只是为了得到最后20行。这是你问题的主要原因。以下将解决您的内存问题:

for line in file(file_directory):
    if find_str in line:
        error = True

这将迭代文件中的所有行,但在处理完后会释放这些行。我猜,这个解决方案已经比你的快得多,所以不需要进一步的优化。但是如果你真的想要最后20行,那么deque中的行最大长度为20。