Python正则表达式 - 减少模式中的冗余?

时间:2012-09-21 07:18:57

标签: python regex functional-programming

我们正在编写一个Python脚本来解析应用程序日志文件。

大多数日志文件都遵循类似的格式:

09:05:00.342344343 [DEBUG] [SOME_APPLICATION] [SOME_FUNCTION] Lorem ipsum dolor sic amet

我们有各种正则表达式来解析通过的不同类型的日志,并将相关字段删除到Python正则表达式组(时间戳,日志级别,原始应用程序/函数以及有效负载中的字段)

我已将这些正则表达式中的每一个存储在一个字典中:

foobar_patterns = {
    'pattern1': re.compile(r'blahblahblah'),
    'pattern2': re.compile(r'blahblahblahblah'),
}

但是,每个模式之间显然有相当多的重叠 - 提取时间戳的正则表达式,日志级别等是共享的。

有没有办法删除这种冗余?你能从一个普通的模板以某种方式建立差异正则表达式字符串吗?

扩展 - 我循环遍历文件中的行,然后为每个给定的行循环遍历每个编译的正则表达式。然后基于此,有不同的功能来处理每种情况 - 例如如果我们检测到某种类型的消息,我们可能需要向前搜索三行以找到其他行,并从中提取一个字段。

我正在考虑在foobar_patterns dict中存储一个函数,然后当我们点击一​​个匹配时,执行该函数。

这是一种做事的Pythonic方法吗?

干杯, 维克多

5 个答案:

答案 0 :(得分:2)

MONTH = r'(?P<month>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)'
DAY = r'(?P<day>\d{2})'
TIME = r'(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})'
SPC = r'\s'
HOST = r'(?<host>\w+)'
PREFIX = SPC.join(MONTH, DAY, TIME, HOST)
foobar_patterns = {
    'pattern1': re.compile(PREFIX + r'\s(?<payload>blahbla hbla h blah)'),
    'pattern2': re.compile(PREFIX + r'\s(?<payload>bla h blahbla hblah)'),
}

答案 1 :(得分:1)

你认为解析两次吗?例如。第一步提取timestamplevelApplicationFunction,然后解析有效负载?您可能需要进行一些缓存,可能首先构建一个已解析的日志对象列表,然后评估已编译的日志消息(使得更容易跳过前面的3行(就像您提到的那样可能是必要的)而无需解析行两次)< / p>

或者你可以使用字符串连接:

伪代码:

basePattern = "\[\w+\]\[\w+\]\[\w+\]"
foobar_patterns {
 'payloadPattern1':'asdf',
 'payloadPattern2':'asdff',
}
for patternKey in foobar_patterns:
    foobar_patterns[patternKey] = re.compile(basePattern + foobar_patterns[paternKey])

答案 2 :(得分:1)

在构建复杂的正则表达式时,我经常使用“语法”方法。首先,您将“语法”定义为dict,例如:

logfile_grammar = {
    'spaces':  '\s+',
    'mname':   '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)',
    'month':   r'\d\d',
    'day':     r'\d\d',
    'year':    r'\d{4}',
    'date':    '(?P<date>($year-$month-$day)|($day $spaces $mname $spaces $year))',
    'payload': '(?P<payload>.+)',
    # more stuff
    'line':    '$date $spaces $payload'
}

如您所见,右侧的$xxx指的是左侧的键(符号)。然后你将这个语法翻译成正则表达式:

def compile_grammar(grammar):
    g = dict(grammar)
    for _ in range(16):
        c = False
        for k, v in g.items():
            w = re.sub(r'\$(\w+)', lambda m: g[m.group(1)], v)
            if w != v:
                g[k] = w
                c = True
        if not c:
            return g
    raise ValueError('too much recursion')

g = compile_grammar(logfile_grammar)    
line_regex = re.compile(g['line'], re.X)

现在,line_regex是一个可以处理任何可能的日志行的正则表达式。

答案 3 :(得分:0)

如果存在大量重叠,请考虑使用一次性提取所有字段的正则表达式:

r'(?P<timestamp>\w*) \[(?P<level>\w*)\] \[(?P<application>\w*)\] ...'

与此正则表达式匹配时,生成的匹配对象将具有groupdict(),您可以从中提取命名组。

答案 4 :(得分:0)

怎么样:

import re

line = '09:05:00.342344343 [DEBUG] [SOME_APPLICATION] [SOME_FUNCTION] Lorem ipsum dolor sic amet'

ts_pattern = r'\d\d:\d\d:\d\d\.\d+'
name_pattern = r'\[\w+\]'

patterns = [('timestamp', ts_pattern),
            ('log_level', name_pattern),
            ('app_name', name_pattern),
            ('func_name', name_pattern),
            ('payload', '.*$')]

line_pattern = r'\s+'.join('(?P<%s>%s)' % (name, pattern) for name, pattern in patterns)

regex = re.compile(line_pattern)

matches = regex.match(line)

print matches.groupdict()

这会给你:

{'timestamp': '09:05:00.342344343', 'log_level': '[DEBUG]', 'payload': 'Lorem ipsum dolor sic amet', 'func_name': '[SOME_FUNCTION]', 'app_name': '[SOME_APPLICATION]'}