我正在编写一个程序来解析一些日志文件。如果该行中包含错误代码,则需要打印前25行以进行分析。我希望能够根据个别错误代码(而不是25行,15或35),用或多或少的行来重复这个概念。
with open(file, 'r') as input:
for line in input:
if "error code" in line:
#print previous 25 lines
我知道Bash中所需的等效命令是grep "error code" -B 25 Filename | wc -1
。我仍然对python和编程尚不熟悉,我知道我将需要一个for
循环,并且我尝试使用range
函数来做到这一点,但是我还没有太多运气不好,因为我不知道如何将范围实现到文件中。
答案 0 :(得分:5)
这是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
)中以使索引有意义。
对于日志文件,您必须假设它可能会很大(我经常处理几千兆字节的日志文件),以至于将其加载到内存中会耗尽主内存,因此,进行大范围包装是一个坏主意,并且每次遇到错误都从头开始重新读取文件几乎一样糟糕(虽然很慢,但是我猜它确实很慢吗?)。基于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
配对的迭代等)是正确的解决方案。
答案 1 :(得分:0)
尝试使用for
循环和range
函数进行基本编码,而无需任何特殊的库:
N = 25
with open(file, 'r') as f:
lines = f.read().splitlines()
for i, line in enumerate(lines):
if "error code" in line:
j = i-N if i>N else 0
for k in range(j,i):
print(lines[k])
上方打印前25行,如果总行少于25,则从第一行开始打印。
此外,最好避免使用input
作为变量,因为它是Python中的关键字。