【问题标题】:Match lines between two patterns in Python with regular expressions [duplicate]用正则表达式匹配Python中两个模式之间的行[重复]
【发布时间】:2019-05-30 02:05:22
【问题描述】:

我正在解析日志文件,其中包含有关许多作业的事件的行,由作业 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.compile() 之后使用re.DEBUG 会显示很多行

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。

【问题讨论】:

  • 您的/0001.server;Considering job to run/,/0040;pbs_sched;Job;0001.server/ 不包含换行符,而要使用awk必须拥有它们,那么什么是现实日志文件内容?
  • 那是正则表达式,不是日志文件。对于 awk,我会使用 awk '/0001.server;Considering job to run/,/0040;pbs_sched;Job;0001.server/' logfile,结果是日志文件中包含的这两个模式之间的行。我可以发布日志,但我最好匿名一些字符串,因为它包含与客户相关的信息。
  • 好的,现在更清楚了

标签: python regex awk


【解决方案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模式关闭时)

【讨论】:

  • 谢谢@Walter。在我的示例中,作业 ID 是 0001。我的问题本来可以更清楚的。
  • 也在上面的评论中...... :-) 但没关系。只需根据您的需要调整正则表达式
【解决方案2】:

/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

【讨论】:

  • 谢谢亚伦。这几乎正​​是我所要求的。但是,pattern1 和 pattern2 在日志中出现了很多次。 awk 命令匹配这些模式之间的所有行。我在ideone中尝试了您的解决方案,输入包含几个这样的块和不相关的块,但它匹配第一个pattern1之后的所有行,也匹配pattern2之后的所有行。
  • @Bubastis 对,我应该看到它的到来。这可以通过使用不情愿版本的* 量词来解决。我已经更新了我的答案和 ideone 测试:)
  • 我不确定选择哪个答案作为正确答案。沃尔特的解决方案应该是首选,但更接近问题的答案是你的。
  • @Bubastis 我认为应该接受 Walter 的 :) 不确定您是否可以接受任何解决方案,因为已链接副本
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多