【问题标题】:How to read the last MB of a very large text file如何读取一个非常大的文本文件的最后一个 MB
【发布时间】:2013-10-03 11:48:10
【问题描述】:

我试图在文本文件末尾附近找到一个字符串。问题是文本文件的大小可能会有很大差异。从 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

【问题讨论】:

    标签: python file text jython


    【解决方案1】:

    使用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 进行更安全的资源管理。

    【讨论】:

    • @nkshakya1 我更新了答案。你根本不需要with;这只是关闭文件的方便。我在评论的最后添加了这一点,以提醒您在完成后关闭它。
    • 或者,如果您有 jython 2.5,您可以在代码顶部添加 from __future__ import with_statement,然后您可以使用 with 语句。
    • 感谢大家的帮助!我认为它现在工作。对于另一种情况,如果我有一个 5-10 kB 的文本文件并且我想查看它的最后 2 kB,我是否只需将 '-1024 * 1024' 替换为 '-2 * 2' ?跨度>
    • @nkshakya1 替换为-2 * 1024 或只是-2048。 (1024 = kB)
    • 谢谢大家!它终于工作了!非常感谢所有的帮助!
    【解决方案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 用作文件行的迭代器,因此整个文件不会一次全部驻留在内存中。

    【讨论】:

    • 不会仍然将整个文件加载到内存中吗? (或者它会像使用生成器表达式一样使用 fin 吗?)
    • fin 将用作生成器,因此整个文件不会同时在内存中。
    • 但是,您不能指定以这种方式回读的字节数吗? (或者也许 OP 不需要那个特定的 1MB)
    • @DaanTimmer:如果您指定可变宽度的行,则不能指定字节;如果您指定包含可变宽度行的文件的字节,则不能指定精确的行。
    【解决方案3】:

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

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

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

    【讨论】:

    • OP 非常清楚地询问了如何读取文件的最后 MB。
    猜你喜欢
    • 2011-10-28
    • 1970-01-01
    • 2015-09-07
    • 2016-01-05
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    • 2011-05-06
    相关资源
    最近更新 更多