我有一个文件,我不知道它会有多大(它可能很大,但尺寸会有很大差异).我想搜索最后10行左右,看看是否有任何一个字符串匹配.我需要尽可能快速有效地做到这一点,并且想知道是否有更好的东西:
s = "foo" last_bit = fileObj.readlines()[-10:] for line in last_bit: if line == s: print "FOUND"
Darius Bacon.. 34
这是一个像MizardX的答案,但没有明显的问题,即在最坏的情况下采用二次时间来重新扫描工作字符串,因为添加了块.
与activestate解决方案(也似乎是二次方)相比,给定一个空文件不会爆炸,并且每个块读取而不是两个.
与产卵'尾巴'相比,这是独立的.(但如果你有它,'尾巴'是最好的.)
相比于从末端抓取几个KB并希望它足够,这适用于任何行长度.
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
编辑:将map()更改为head()中的itertools.imap().编辑2:简化reverse_blocks().编辑3:避免重新扫描新行的尾部.编辑4:重写者reverse_lines()因为str.splitlines()忽略了最后的'\n',正如BrianB注意到的那样(谢谢).
请注意,在非常旧的Python版本中,循环中的字符串连接将采用二次时间.CPython至少在过去几年中自动避免了这个问题.
这是一个像MizardX的答案,但没有明显的问题,即在最坏的情况下采用二次时间来重新扫描工作字符串,因为添加了块.
与activestate解决方案(也似乎是二次方)相比,给定一个空文件不会爆炸,并且每个块读取而不是两个.
与产卵'尾巴'相比,这是独立的.(但如果你有它,'尾巴'是最好的.)
相比于从末端抓取几个KB并希望它足够,这适用于任何行长度.
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
编辑:将map()更改为head()中的itertools.imap().编辑2:简化reverse_blocks().编辑3:避免重新扫描新行的尾部.编辑4:重写者reverse_lines()因为str.splitlines()忽略了最后的'\n',正如BrianB注意到的那样(谢谢).
请注意,在非常旧的Python版本中,循环中的字符串连接将采用二次时间.CPython至少在过去几年中自动避免了这个问题.
# 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
如果您在POSIX系统上运行Python,则可以使用'tail -10'来检索最后几行.这可能比编写自己的Python代码以获得最后10行更快.而不是直接打开文件,从命令'tail -10 filename'打开一个管道.如果您确定日志输出(例如,您知道从来没有任何超长数百或数千个字符的长行),那么使用列出的"读取最后2KB"方法之一就可以了.
我认为读取文件的最后2 KB左右应该确保你获得10行,并且不应该过多地占用资源.
file_handle = open("somefile") file_size = file_handle.tell() file_handle.seek(max(file_size - 2*1024, 0)) # this will get rid of trailing newlines, unlike readlines() last_10 = file_handle.read().splitlines()[-10:] assert len(last_10) == 10, "Only read %d lines" % len(last_10)
这是一个使用mmap
它的版本似乎非常有效.最重要的是,mmap
它将自动处理文件到内存的分页要求.
import os from mmap import mmap def lastn(filename, n): # open the file and mmap it f = open(filename, 'r+') m = mmap(f.fileno(), os.path.getsize(f.name)) nlcount = 0 i = m.size() - 1 if m[i] == '\n': n += 1 while nlcount < n and i > 0: if m[i] == '\n': nlcount += 1 i -= 1 if i > 0: i += 2 return m[i:].splitlines() target = "target string" print [l for l in lastn('somefile', 10) if l == target]