質問に「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番目の文字で正しく停止することを示すためにソース文字列に追加しました)まで読み上げましたが、これは含まれていません。解析アクションを追加しました。このアクションは、式が一致したときに解析時のコールバックとして実行されます。一致したトークンは解析アクションに渡され、アクションが値を返す場合、その値が出力内の解析されたトークンを置き換えます。ここでは、単純なラムダを使用して最初のトークン(文字の配列)を取得し、それらを1つの文字列に結合します。
しかし、フィールド「作成者」と「日付」を抽出したいとも言っていました。これらは実際にはlog_messageで抽出されたものの一部であるため、その文字列を別の式に渡す必要があります。幸い、2番目の解析アクションでそのようなことを行うことができます。
この第2フェーズの解析では、次の形式のキー付きの値をとるパーサーを作成することにしました。
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))
SkipTo
それでも、そのログメッセージが必要です。これは、文字列の最後まで他のすべてを取得するためにpyparsingを使用するのが最も簡単です。
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を解析する2番目の解析アクションを追加します。
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