使用正则表达式匹配Python中两个模式之间的行

时间:2019-01-03 13:15:03

标签: python regex awk

我正在解析日志文件,其中包括有关许多作业的事件的行,这些行由作业ID标识。我正在尝试在Python中的两种模式之间获取日志文件中的所有行。

我已经阅读了这篇非常有用的帖子How to select lines between two patterns?,并且已经像这样解决了awk的问题:

awk '/pattern1/,/pattern2/' file

由于我正在用Python脚本处理日志信息,因此我正在使用subprocess.Popen()执行该awk命令。我的程序可以运行,但我想单独使用Python来解决。

我知道re模块,但不太了解如何使用它。日志文件已经被压缩到bz2,所以这是我的代码,用于打开.bz2文件并查找两种模式之间的线:

import bz2
import re

logfile = '/some/log/file.bz2'

PATTERN = r"/{0}/,/{1}/".format('pattern1', 'pattern2')
# example: PATTERN = r"/0001.server;Considering job to run/,/0040;pbs_sched;Job;0001.server/"
re.compile(PATTERN)

with bz2.BZ2File(logfile) as fh:
    match = re.findall(PATTERN, fh.read())

但是,match为空(fh.read()不是!)。使用re.findall(PATTERN, fh.read(), re.MULTILINE)无效。 在re.DEBUG之后使用re.compile()会显示许多行,

literal 47
literal 50
literal 48
literal 49
literal 57

两个说

any None

我可以使用python print between two patterns, including lines containing patterns这样的循环来解决问题,但是我会尽量避免嵌套的for-if循环。我相信re模块可以产生我想要的结果,但是我不是如何使用它的专家。

我正在使用Python 2.7.9。

2 个答案:

答案 0 :(得分:1)

将整个日志文件读入内存通常不是一个好主意,因此我将为您提供逐行解决方案。我假设您的示例中的点是模式中唯一变化的部分。我还要假设您要在列表列表中收集线路组。

import bz2
import re

with_delimiting_lines = True
logfile = '/some/log/file.bz2'
group_start_regex = re.compile(r'/0001.server;Considering job to run/')
group_stop_regex  = re.compile(r'/0040;pbs_sched;Job;0001.server/')
group_list = []
with bz2.BZ2File(logfile) if logfile.endswith('.bz2') else open(logfile) as fh:
    inside_group = False
    for line_with_nl in fh:
        line = line_with_nl.rstrip()
        if inside_group:
            if group_stop_regex.match(line):
                inside_group = False
                if with_delimiting_lines:
                    group.append(line)
                group_list.append(group)
            else:
                group.append(line)
        elif group_start_regex.match(line):
            inside_group = True
            group = []
            if with_delimiting_lines:
                group.append(line)

请注意,match()从行的开头开始匹配(就像在^模式关闭的情况下,模式以re.MULTILINE开头)

答案 1 :(得分:1)

/pattern1/,/pattern2/不是正则表达式,而是特定于awk的结构,该结构由两个正则表达式组成。

使用纯正则表达式,您可以将pattern1.*?pattern2DOTALL标志一起使用(这会使.在通常不匹配的情况下匹配换行符):

re.findall("pattern1.*?pattern2", input, re.DOTALL)

它不同于awk命令,后者将匹配包含开始和结束模式的整行;这可以通过以下方式实现:

re.findall("[^\n]*pattern1.*?pattern2[^\n]*", input, re.DOTALL)

Try it here !

请注意,我回答您的问题是出于教学的考虑,但是Walter Tross' solution应该是首选。