彻底解析所有匹配的文件

时间:2018-04-12 22:21:37

标签: python pyparsing

我有一个使用pyparsing解析一些日志文件的语法,但遇到了只返回第一个匹配的问题。有没有办法确保我得到详尽的比赛?这是一些代码:

from pyparsing import Literal, Optional, oneOf, OneOrMore, ParserElement, Regex, restOfLine, Suppress, ZeroOrMore

ParserElement.setDefaultWhitespaceChars(' ')
dt = Regex(r'''\d{2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) 20\d\d \d\d:\d\d:\d\d\,\d{3}''')
# TODO maybe add a parse action to make a datetime object out of the dt capture group
log_level = Suppress('[') + oneOf("INFO DEBUG ERROR WARN TRACE") + Suppress(']')
package_name = Regex(r'''(com|org|net)\.(\w+\.)+\w+''')
junk_data = Optional(Regex('\(.*?\)'))
guid = Regex('[A-Za-z0-9]{8}-[A-Za-z0-9]{4}-[A-Za-z0-9]{4}-[A-Za-z0-9]{4}-[A-Za-z0-9]{12}')

first_log_line = dt.setResultsName('datetime') +                    \
            log_level('log_level') +                                \
            guid('guid') +                                          \
            junk_data('junk') +                                     \
            package_name('package_name') +                          \
            Suppress(':') +                                         \
            restOfLine('message') +                                 \
            Suppress('\n')
additional_log_lines = Suppress('\t') + package_name + restOfLine
log_entry = (first_log_line + Optional(ZeroOrMore(additional_log_lines)))
log_batch = OneOrMore(log_entry)

在我看来,最后两行等同于

log_entry := first_log_line | first_log_line additional_log_lines
additional_log_lines := additional_log_line | additional_log_line additional_log_lines
log_batch := log_entry | log_entry log_batch

或类似的东西。我在想这个错吗?当我print(log_batch.parseString(data).dump())时,我只看到与所有预期令牌的单一匹配。

2 个答案:

答案 0 :(得分:1)

所以,有一个似乎可以解决问题的解决方法。无论出于何种原因,scanString 都会适当地迭代它们,因此我可以非常简单地在生成器中获取匹配项:

matches = (m for m, _, _ in log_batch.scanString(data))

仍然不确定为什么parseString 不是无法正常工作,但仍然有点担心我误解了有关pyparsing的事情,所以欢迎更多指针。

答案 1 :(得分:1)

您的scanString行为是一个强有力的线索。假设我写了一个表达式来匹配一个或多个项目,并错误地定义了我的表达式,使得列表中的第二项不匹配。然后OneOrMore(expr)会失败,而expr.scanString会“成功”,因为它会给我更多匹配,但仍然会忽略我可能想要的匹配,但只是错误-parsed。

import pyparsing as pp

data = "AAA _AB BBB CCC"

expr = pp.Word(pp.alphas)
print(pp.OneOrMore(expr).parseString(data))

给出:

['AAA']

乍一看,这似乎是OneOrMore失败,而scanString显示更多匹配:

['AAA']
['AB']  <- really wanted '_AB' here
['BBB']
['CCC']

这是一个使用scanString的循环,它不打印匹配项,但是匹配项之间的间隙,以及它们的开始位置:

# loop to find non-matching parts in data
last_end = 0
for t,s,e in expr.scanString(data):
    gap = data[last_end:s]
    print(s, ':', repr(gap))
    last_end = e

,并提供:

0 : ''
5 : ' _'  <-- AHA!!
8 : ' '
12 : ' '

这是另一种可视化的方法。

# print markers where each match begins in input string
markers = [' ']*len(data)
for t,s,e in expr.scanString(data):
    markers[s] = '^'

print(data)
print(''.join(markers))

打印:

AAA _AB BBB CCC
^    ^  ^   ^  

您的代码会更复杂,因为您的数据跨越多行,但使用pyparsing的{​​{1}},linelineno方法,您可以执行某些操作类似。