您使用“pyparsing”标签标记了您的问题,因此您可以使用 pyparsing 来解决它。Pyparsing 包含一个名为的辅助方法countedArray
,它几乎可以满足您的需求。你可以想到:
countedArray(something)
作为快捷方式:
integer + something*(whatever was parsed as the integer)
并将解析的数据作为某些东西的列表返回。
source = """commit a8c11fcee68881dfb86095aa36290fb304047cf1
log size 110
Author: XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>
Date: Tue, 10 Apr 2012 11:19:44 +0300
First commit<not in the message, more than 110 chars>
3 0 README.MD
"""
from pyparsing import *
any_char = Word(printables+" \n",exact=1).leaveWhitespace()
log_message = countedArray(any_char)
# we want a string, not an array of single characters
log_message.setParseAction(lambda t: ''.join(t[0]))
entry = "commit" + Word(hexnums)('id') + "log size" + log_message
msg = entry.parseString(source)[-1]
print (msg)
给出:
Author: XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>
Date: Tue, 10 Apr 2012 11:19:44 +0300
First commit
您可以看到我们已经阅读了但不包括“不在消息中...”部分(我将其添加到您的源字符串中以显示countedArray
正确停止在第 110 个字符处)。我添加了一个解析操作,当表达式匹配时,pyparsing 将作为解析时回调运行。匹配的标记被传递给解析操作,如果操作返回一个值,该值将替换输出中的已解析标记。在这里,我们使用一个简单的 lambda 来获取第一个标记(字符数组),并将它们连接成一个字符串。
但是您也说过要提取“作者”和“日期”字段。它们实际上是在 log_message 中提取的内容的一部分,因此您必须将该字符串传递给另一个表达式。幸运的是,您可以在第二个解析操作中执行此类操作。
在这个第二阶段解析中,我决定创建一个解析器,它将采用以下形式的任何键值:
some key: the value of that key up to the end of the line
如果“作者”和“日期”只是您可能在源文本中找到的任意数量的键的示例。Pyparsing 也有命名结果,类似于正则表达式中的命名组。通常,当名称已知时,您只需在上面显示的名称上加上您的提交 ID。但是在您的日志消息中,实际名称是从输入本身解析出来的,因此为此,pyparsing 有一个Dict
类。该类Dict
将采用一组已解析的组,并用每个组的名称装饰该数据,将组的第一个元素作为名称,将组的其余部分作为值。因此,我们希望显示每个键值的名称,其中 ':' 之前的所有内容都将是名称,跳过冒号和任何前导空格,然后将行的其余部分作为值。
COLON = Suppress(':')
keyed_value = Group(Word(printables+' ',excludeChars=':') + COLON + empty + restOfLine)
keyed_entries = Dict(ZeroOrMore(keyed_value))
我们仍然想要该日志消息,最简单的是使用 pyparsingSkipTo
将其他所有内容都带到字符串的末尾:
everything_else_up_to_the_end_of_the_string = SkipTo(StringEnd())
这是您的日志消息正文的语法:
log_message_body = keyed_entries +
everything_else_up_to_the_end_of_the_string('message')
我们已经有了一个解析动作,可以将 log_message 中的所有解析字符组合成一个字符串,但是可以将多个解析动作链接到一个表达式上。我们将添加第二个解析操作,它将使用 log_message_body 语法解析解析的 log_message:
def parseMessage(tokens):
return log_message_body.parseString(tokens[0])
log_message.addParseAction(parseMessage)
现在我们可以再次运行完整的解析器,这一次,输出结果和它们的名字。可以像访问对象的属性一样访问命名的结果(或者如果您愿意,可以使用 dict 表示法):
log_entry = entry.parseString(source)
print (log_entry.id)
print (log_entry['message'])
print (log_entry.dump())
给出:
a8c11fcee68881dfb86095aa36290fb304047cf1
First commit
['commit', 'a8c11fcee68881dfb86095aa36290fb304047cf1', '
log size', ['Author', 'XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>'],
['Date', 'Tue, 10 Apr 2012 11:19:44 +0300'],
'First commit']
- Author: XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>
- Date: Tue, 10 Apr 2012 11:19:44 +0300
- id: a8c11fcee68881dfb86095aa36290fb304047cf1
- message: First commit