在 python 行中找到的匹配字符串后打印前几行

Print a number of previous lines after matching string found in line in python

我正在编写一个程序来解析一些日志文件。如果行中有错误代码,我需要打印前 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 函数来执行此操作,但我还没有运气不好,因为我不知道如何将范围实现到文件中。`

这是 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 的 good/safe 方式解决,因为索引只有在将整个文件加载到内存中时才有意义;磁盘上的文件不知道行的开始和结束位置,因此您不能只要求 "line #357 of the file" 而不从头开始阅读以找到第 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 的索引以限制您的切片。

请注意,即便如此,我也没有使用 rangerange 是很多 C 出身的人所依赖的拐杖,但它通常是 Python 中解决问题的错误方法。在 需要 索引的情况下(通常不需要),您通常也需要该值,因此基于 enumerate 的解决方案更胜一筹;大多数时候,您根本不需要索引,因此直接迭代(或与 zip 等配对迭代)是正确的解决方案。

尝试在没有任何特殊库的情况下使用 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 中的关键字。