搜索文件最后X行的最有效方法是什么?

33 投票
19 回答
45134 浏览
提问于 2025-04-11 20:06

我有一个文件,但我不知道它会有多大(可能会很大,但大小会有很大差异)。我想查看最后大约10行,看看其中有没有和某个字符串匹配的内容。我需要尽可能快和高效地完成这个操作,想知道有没有比下面这个更好的方法:

s = "foo"
last_bit = fileObj.readlines()[-10:]
for line in last_bit:
    if line == s:
        print "FOUND"

19 个回答

9

如果你在一个POSIX系统上运行Python,可以用'tail -10'这个命令来获取文件的最后几行。这种方法可能比自己写Python代码来获取最后10行要快。与其直接打开文件,不如通过命令'tail -10 文件名'来打开一个管道。不过,如果你对日志输出很有把握(比如,你知道里面绝对不会有几百或几千个字符的长行),那么使用一些“读取最后2KB”的方法也是可以的。

36

这里有一个答案,类似于MizardX的,但没有它在最坏情况下因为重复扫描工作字符串寻找换行符而导致的时间复杂度问题。

与Active State的解决方案相比(它似乎也有时间复杂度问题),这个方法在处理空文件时不会出现性能爆炸,并且每次读取一个块时只需要一次查找,而不是两次。

与启动'tail'命令相比,这个方法是自包含的。(不过如果你有'tail',那它是最好的选择。)

与从文件末尾抓取几千字节并希望它足够的做法相比,这个方法适用于任何行长度。

import os

def reversed_lines(file):
    "Generate the lines of file in reverse order."
    part = ''
    for block in reversed_blocks(file):
        for c in reversed(block):
            if c == '\n' and part:
                yield part[::-1]
                part = ''
            part += c
    if part: yield part[::-1]

def reversed_blocks(file, blocksize=4096):
    "Generate blocks of file's contents in reverse order."
    file.seek(0, os.SEEK_END)
    here = file.tell()
    while 0 < here:
        delta = min(blocksize, here)
        here -= delta
        file.seek(here, os.SEEK_SET)
        yield file.read(delta)

按照要求使用它:

from itertools import islice

def check_last_10_lines(file, key):
    for line in islice(reversed_lines(file), 10):
        if line.rstrip('\n') == key:
            print 'FOUND'
            break

编辑:在head()中将map()改为itertools.imap()。编辑2:简化了reversed_blocks()。编辑3:避免了对尾部进行重复扫描以寻找换行符。编辑4:重写了reversed_lines(),因为str.splitlines()会忽略最后的'\n',正如BrianB所指出的(谢谢)。

请注意,在非常旧的Python版本中,这里在循环中进行字符串连接会导致时间复杂度问题。至少在过去几年的CPython中,这个问题会自动避免。

38
# Tail
from __future__ import with_statement

find_str = "FIREFOX"                    # String to find
fname = "g:/autoIt/ActiveWin.log_2"     # File to check

with open(fname, "r") as f:
    f.seek (0, 2)           # Seek @ EOF
    fsize = f.tell()        # Get Size
    f.seek (max (fsize-1024, 0), 0) # Set pos @ last n chars
    lines = f.readlines()       # Read to end

lines = lines[-10:]    # Get last 10 lines

# This returns True if any line is exactly find_str + "\n"
print find_str + "\n" in lines

# If you're searching for a substring
for line in lines:
    if find_str in line:
        print True
        break

当然可以!请把你想要翻译的内容发给我,我会帮你把它变得更简单易懂。

撰写回答