这是a length limited collections.deque 的完美用例:
from collections import deque
line_history = deque(maxlen=25)
with open(file) as input:
for line in input:
if "error code" in line:
print(*line_history, line, sep='')
# Clear history so if two errors seen in close proximity, we don't
# echo some lines twice
line_history.clear()
else:
# When deque reaches 25 lines, will automatically evict oldest
line_history.append(line)
我为什么选择这种方法的完整解释(如果你不关心,请跳过):
使用for/range 无法以良好/安全的方式解决此问题,因为索引只有在将整个文件加载到内存时才有意义;磁盘上的文件不知道行的开始和结束位置,因此您不能只要求“文件的第 357 行”而不从头开始读取它以找到第 1 行到第 356 行。您最终会反复重读文件,或将整个文件放入内存序列(例如list/tuple)以使索引有意义。
对于一个日志文件,您必须假设它可能非常大(我经常处理数 GB 的日志文件),以至于将其加载到内存中会耗尽主内存,所以 slurping 是一个坏主意,并且每次遇到错误时从头开始重新读取文件几乎一样糟糕(它很慢,但我猜它确实很慢?)。基于deque 的方法意味着您的峰值内存使用量基于文件中最长的 27 行,而不是文件总大小。
只有内置插件的天真的解决方案可能很简单:
with open(file) as input:
lines = tuple(input) # Slurps all lines from file
for i, line in enumerate(lines):
if "error code" in line:
print(*lines[max(i-25, 0):i], line, sep='')
但就像我说的那样,这需要足够的内存来一次将整个日志文件保存在内存中,这是一件很糟糕的事情。当两个错误非常接近时,它也会重复行,因为与deque 不同,你没有一个简单的方法来清空你最近的记忆;您必须手动跟踪最后一个 print 的索引以限制您的切片。
请注意,即便如此,我也没有使用range; range 是许多来自 C 背景的人所依赖的拐杖,但它通常是用 Python 解决问题的错误方法。在需要(通常不需要)索引的情况下,您通常也需要该值,因此基于enumerate 的解决方案更胜一筹;大多数时候,您根本不需要索引,因此直接迭代(或与zip 或类似的配对迭代)是正确的解决方案。