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

时间:2018-08-24 18:16:00

标签: python text-parsing fileparsing

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

2 个答案:

答案 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的索引才能限制切片。

请注意,即使那样,我也没有使用rangerange是很多来自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中的关键字。