我有一个使用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())
时,我只看到与所有预期令牌的单一匹配。
答案 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}},line
和lineno
方法,您可以执行某些操作类似。