2

次の文字列があります。

commit a8c11fcee68881dfb86095aa36290fb304047cf1
log size 110
Author: XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>
Date:   Tue, 10 Apr 2012 11:19:44 +0300

    First commit

3       0       README.MD

文法の定義の値を使用して110、残りのものと一致させるにはどうすればよいですか?「ログサイズ」には、フィールド(ここでは:AuthorDate、ただしフィールドはいくつでもかまいません)と実際のメッセージが含まれます。

最後の行は「ログメッセージ」の一部ではありません。

取得したいのは、の値、となどのcommitメタデータを含む辞書、AuthorおよびDate実際のログメッセージ(ここでは「最初のコミット」)です。

問題は、log sizeこのメッセージの長さを教えてくれますが、これにはフィールドAuthorも含まDateれます。

110この文字列のサイズです:

Author: XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>
Date:   Tue, 10 Apr 2012 11:19:44 +0300

    First commit
4

3 に答える 3

2

私は3つの段階でそれを行います:

  1. 正規表現を使用して各コミットを検索し、そのIDとログサイズを取得します。
  2. 手順1の一致の終了とログサイズを使用して、文字列からメタデータとメッセージをスライスします。
  3. 手順2の文字列を辞書とメッセージに解析します。

最初の2つの手順は、次のように実行できます。

In [25]: s = """commit a8c11fcee68881dfb86095aa36290fb304047cf1
log size 110
Author: XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>
Date:   Tue, 10 Apr 2012 11:19:44 +0300

    First commit
3       0       README.MD
"""

In [26]: m = re.search('commit (.*)\nlog size (.*)\n', s)

In [27]: s[m.end():m.end()+int(m.group(2))]
Out[27]: 'Author: XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>\nDate:   Tue, 10 Apr 2012 11:19:44 +0300\n\n    First commit\n'

最後の文字列が呼び出された場合step2、残りの解析は次のように実行できます。

In [48]: meta, msg = step2.split('\n\n', 1)

In [49]: dict([map(str.strip, line.split(':', 1)) for line in meta.split('\n')])
Out[49]: 
{'Author': 'XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>',
 'Date': 'Tue, 10 Apr 2012 11:19:44 +0300'}

In [50]: msg
Out[50]: '    First commit\n'
于 2012-12-01T11:36:11.857 に答える
2

私はNPEと同じアルゴリズムの考えを持っていました。
しかし、私は正規表現の使用をさらに推し進めました。

'log size xxx \ n'行に正しい文字数を入れるように注意しながら、分析されたテキストを2回目のログメッセージで拡張しました

regex1は、各オカレンスを4つのグループにカットします。3番目のグループにはディクショナリがある行が含まれ、4番目のグループにはディクショナリ行の後と他のオカレンスの前の末尾の行があります。

import re

ss = """commit a8c11fcee68881dfb86095aa36290fb304047cf1
log size 110
Author: XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>
Date:   Tue, 10 Apr 2012 11:19:44 +0300

    First commit
3       0       README.MD
blablah bla
commit 12458777AFDRE1254
log size 170
   Author: Jim Bluefish <jimblfsh@gmail.com>
Date   :   Yesterday 21:45:01 +0800
  A key with whitespace :       A_stupid_value    

    Funny commit
  From far from you
457      popo       not_README.MD"""

n = 0
print ('------ DISPLAY OF THE TEXT ------\n'
       ' col 1: index of line,\n'
       ' col 2: number of chars in the line\n'
       ' col 3: total of the numbers of chars of lines\n'
       ' col 4: repr(line)\n')
for j,line in enumerate(ss.splitlines(1)):
    n += len(line)
    print '%2d  %2d  %3d  %r' % (j,len(line),n,line)


print '=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-='
print '\n\n\n------ ANALYSER 2 OF THE TEXT ------'

regx1 = re.compile('^commit +(.+) *\r?\n'
                   'log size +(\d+) *\r?\n'
                   '((?:^ *.+?(?<! ) *: *.+(?<! ) *\r?\n)+)'
                   '((?:.*\r?\n(?!commit))+)',
                   re.MULTILINE)

regx2 = re.compile('^ *(.+?)(?<! ) *: *(.+)(?<! ) *\r?\n',
                   re.MULTILINE)

for mat in regx1.finditer(ss):

    commit_value,logsize,dicolines,msg = mat.groups()

    print ('\ncommit_value == %s\n'
           'logsize == %s'
           % (commit_value,logsize))

    print 'dictionary :\n',dict(regx2.findall(dicolines))

    actual_log_message = msg[0:int(logsize)-len(dicolines)].strip(' \r\n')
    print 'actual_log_message ==',repr(actual_log_message)

結果

------ DISPLAY OF THE TEXT ------
 col 1: index of line,
 col 2: number of chars in the line
 col 3: total of the numbers of chars of lines
 col 4: repr(line)

 0  48   48  'commit a8c11fcee68881dfb86095aa36290fb304047cf1\n'
 1  13   61  'log size 110\n'
 2  52  113  'Author: XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>\n'
 3  40  153  'Date:   Tue, 10 Apr 2012 11:19:44 +0300\n'
 4   1  154  '\n'
 5  17  171  '    First commit\n'
 6  26  197  '3       0       README.MD\n'
 7  12  209  'blablah bla\n'
 8  25  234  'commit 12458777AFDRE1254\n'
 9  13  247  'log size 170\n'
10  45  292  '   Author: Jim Bluefish <jimblfsh@gmail.com>\n'
11  36  328  'Date   :   Yesterday 21:45:01 +0800\n'
12  51  379  '  A key with whitespace :       A_stupid_value    \n'
13   1  380  '\n'
14  17  397  '    Funny commit\n'
15  20  417  '  From far from you\n'
16  33  450  '457      popo       not_README.MD'



------ ANALYSER OF THE TEXT ------

commit_value == a8c11fcee68881dfb86095aa36290fb304047cf1
logsize == 110
dico :
{'Date': 'Tue, 10 Apr 2012 11:19:44 +0300', 'Author': 'XXXXXX XXXXXXXX <XXXXXXXXXXXXXXX@XXXXX.XXX>'}
actual_log_message == 'First commit'


commit_value == 12458777AFDRE1254
logsize == 170
dico :
{'Date': 'Yesterday 21:45:01 +0800', 'A key with whitespace': 'A_stupid_value', 'Author': 'Jim Bluefish <jimblfsh@gmail.com>'}
actual_log_message == 'Funny commit\n  From far from you'
于 2012-12-01T13:50:07.693 に答える
0

質問に「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

「メッセージに含まれていません...」の部分(countedArray110番目の文字で正しく停止することを示すためにソース文字列に追加しました)まで読み上げましたが、これは含まれていません。解析アクションを追加しました。このアクションは、式が一致したときに解析時のコールバックとして実行されます。一致したトークンは解析アクションに渡され、アクションが値を返す場合、その値が出力内の解析されたトークンを置き換えます。ここでは、単純なラムダを使用して最初のトークン(文字の配列)を取得し、それらを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
于 2012-12-07T09:57:52.717 に答える