2016-06-14 7 views
2

キーと値のペアとしてフォーマットされた文字列のリストをスペースで区切ります。たとえば、メッセージは次のようになります。Python:フォーマットされた文字列からフィールドを取り出します

"time=2016/06/14 16:44:00.000 level=1 sequenceNum=35 user=Username subject=subject goes here message=This is a message" 

キーと値のペアは常にこの順序になり、メッセージは常にこの形式になります。私はこの形で辞書にこの文字列を変換したい:

{'level': 1, 
'message': 'This is a message', 
'sequenceNum': 35, 
'subject': 'subject goes here', 
'time': '2016/06/14 16:44:00.000', 
'user': 'Username'} 

注意するカップルの事:

  1. 私がすることができlevelsequenceNumは数字ではなく文字列
  2. になりたいですタイムスタンプのスペース、件名、メッセージはスペースで分けることができません
  3. メッセージと件名には何かが含まれる可能性がありますので、または等号。しかし、それらは常に文字列の最後と最後の2番目のものになります。文字列'message='が含まれている可能性がある件名の問題を解決できれば、件名の終わりとメッセージの開始位置を区別することができませんが、それは素晴らしいことですが、現在はその問題を無視しています。

現在、私が持っている最高のはこれです:

item = {} 
item['time'] = message[5:message.index('level=')].strip() 
message = message[message.index('level='):] 
item['level'] = int(message[6:message.index('sequenceNum=')].strip()) 
message = message[message.index('sequenceNum='):] 
#etc. 

私はそれが明らかに正常に動作していても、実際には、このようにしないでください。私は文字列の書式設定に基づいてそれを行うよりエレガントな方法があることを望んでいました。私は、この文字列を作成しようとしていた場合たとえば、私はこれを使用することができます。それは、他の方向にそれを行うことが可能です場合、私は思ったんだけど

"time=%s level=%s sequenceNum=%s user=%s subject=%s message=%s" % (item['time'], item['level'], item['sequenceNum'], item['user'], item['subject'], item['message']) 

。 「=」の分割、前の行の最後に各ラベルを残し:

+1

値には何も含めることができますか?これは有効な主題ですか? 'subject = a message =これはmessage = subject = message = 1 = hello'です。値文字列に '= '文字を使用しないでください。 –

+0

@件名とメッセージの@HåkenLidの値には何も含めることができます。残りはより確実に固定されます。はい、あなたの例は有効な主題です。私は '='と件名とメッセージのラベルを扱いたいと思います。私が今無視しようとしているのは、 '' message = "'を含む主題だけです。それはメッセージの始まりと区別することは不可能です。しかし、 '' subject = "'を含むメッセージは、ラベルが固定され、順序付けされているため、管理が容易です。 – ewok

+0

そのばかげたメッセージ形式を思いついた人が解雇されたことを願っています。 –

答えて

2

このため、私は正規表現に行きます。それは最も速い(パフォーマンス上の)ソリューションではないかもしれませんし、(理解するのが最も簡単な)ソリューションではないかもしれませんが、確かにうまくいくでしょう。 (そして、おそらくあなたは、「リバース・フォーマット」になります最も近い)あなたはresult['level'] = int(result['level'])ような何かを行うことができ数値として番号を取得するには

import re 

pattern = re.compile(
    "time=(?P<time>.+)\s" 
    "level=(?P<level>\d+)\s" 
    "sequenceNum=(?P<sequenceNum>\d+)\s" 
    "user=(?P<user>\w+)\s" 
    "subject=(?P<subject>.+?)\s" # <-- EDIT: changed from greedy '.+' to non-greedy '.+?' 
    "message=(?P<message>.+)" 
    ) 

lines = ["time=2016/06/14 16:44:00.000 level=1 sequenceNum=35 user=Username subject=subject goes here message=This is a message", 
     "time=2016/06/14 16:44:00.000 level=1 sequenceNum=35 user=Username subject=subject goes here message=This is a message=hello"] 

for line in lines: 
    match = pattern.match(line) 
    item = match.groupdict() 
    print(item) 

興味があれば、正規表現をどのように構築し、どのように改善できるかを少し詳しく説明します。

EDIT:message=のエッジケースを対象に変更しました。

+0

これは私が欲しいものをほとんど得ます。唯一の問題は、メッセージに文字列 '' message = "'が含まれている場合です。例えば、 '' time = 2016/06/14 16:44:00.000 level = 1 sequenceNum = 35 user =ユーザー名subject =件名はここにありますmessage =これはメッセージ= hello "'](http:// pastebin。 com/Pj8tKqLY)。この正規表現を強制して、件名を停止し、最後の '' message = "'の最初の出現でメッセージを開始できますか? – ewok

+0

@ewok:私はこれをカバーするために私の解決策を編集しました。 – PeterE

0

okが、すぐに一緒に

inputString = "time=2016/06/14 16:44:00.000 level=1 sequenceNum=35 user=Username subject=subject goes here message=This is a message" 

keys = [] 
#key is always the element before the '=' sign 
for segment in inputString.split('='): 
    keys.append(segment.split(" ")[-1]) 

values = inputString.split("=") 
for i in range(len(values)): 
    #split the values by the spaces 
    tmp = values[i].split(" ") 
    #and remove the last part --> the part before the equals sign 
    tmp.pop(-1) 
    #join them back together 
    values[i] = ' '.join(tmp) 
#the first element is now empty, because there is no value before the first '=' 
values.pop(0) 

#the last element will be missing in this case, because it will be interpretet as yet another key 
if ' ' in inputString.split("=")[-1]: 
    values[-1] += ' '+inputString.split("=")[-1].split(' ')[-1] 
else: 
    #if the last element does not contain a space it will be missing entirely --> adding it back in 
    values.pop(-1) 
    values += inputString.split("=")[-1] 

# combining it to a dict 
outputDict = dict(zip(keys, values)) 
print(outputDict) 
0

何か少し水平思考を投げました。たとえば:

in_stuff = "time=2016/06/14 16:44:00.000 level=1 sequenceNum=35" 
      +" user=Username message=This is a message" 
skew = in_stuff.split('=') 
table = [entry.split() for entry in skew] 
out_dict = {table[i][-1] : ' '.join(table[i+1][:-1]) 
      for i in range(len(table)-1)} 
print out_dict 

これはあまり行われていませんが、そのアイデアを示しています。出力:

{'sequenceNum': '35', 
'level': '1', 
'message': 'This is a', 
'user': 'Username', 
'time': '2016/06/14 16:44:00.000'} 

あなたはまだ数字を変換し、そしてテーブルの最後の行からのメッセージの最後の言葉を回復する必要があります。私はこれらをインラインで行うことができましたが、プレゼンテーションを少し詰まらせると思っていました。

2

正規表現とre.findall()を使用して、各キーと値のペアを見つけることができます。この方法の利点は、key=valueペアの任意の文字列で動作することです。出力で

import re 
data = ("time=2016/06/14 16:44:00.000 level=1 sequenceNum=35 " 
     "user=Username subject=subject goes here message=This is a message") 
matches = re.findall(r'(\w+)=([^=]+)(?:\s|\Z)', data) 
{key: int(val) if val.isdigit() else val for key, val in matches} 

、整数のように見えるすべての値は、intに変換されています。

{'level': 1, 
'message': 'This is a message', 
'sequenceNum': 35, 
'subject': 'subject goes here', 
'time': '2016/06/14 16:44:00.000', 
'user': 'Username'} 

あなたは整数に数値を変換する必要がなかった場合、それがさらに簡単だっただろう:

dict(re.findall(r'(\w+)=([^=]+)(?:\s|\Z)', data)) 

ここにあなたの入力の場合、正規表現(\w+)=([^=]+)(?:\s|\Z)

のregex101説明がありますデータには次のようなものが含まれています:"subject=message=subject=message=subject"、あいまいなので問題があります。入力をサニタイズするか、例外を発生させるだけです。

if data.count('=') != 6: 
    raise ValidationError('malformed input data: {}'.format(data)) 
+0

これは私が欲しいものをほとんど得ます。唯一の問題は、メッセージに文字列 '' message = "'が含まれている場合です。例えば、 '' time = 2016/06/14 16:44:00.000 level = 1 sequenceNum = 35 user =ユーザー名subject =件名はここにありますmessage =これはメッセージ= hello "'](http:// pastebin。 com/m8wbxZfa)。この正規表現を強制して、件名を停止し、最後の '' message = "'の最初の出現でメッセージを開始できますか? – ewok

+0

このアプローチは、「デコイ」キーと値のペアがない入力に依存します。この式を実行する前に入力をサニタイズすることができます。たとえば、いくつかの '= '文字をurl-encodeします。あるいは、他の回答に示唆されているような、より具体的な(しかし複雑な)正規表現を使用することもできます。 –

0

カスタムコンバーターを使用できるようになりました。

class Convert(object): 
    def __init__(self, *args): 
     self.content = ' '.join(args) 

    def sequenceNum(self): 
     return int(self.content) 

    def level(self): 
     return int(self.content) 

    def __getattr__(self, name): 
     def wrapper(*args, **kwargs): 
      return self.content 
     return wrapper 


def line_to_dict(s): 
    r = {} 
    s_split = s.split('=') 
    l = len(s_split) - 1 
    k = None 

    for i, content in enumerate(s_split): 
     if not i: 
      k = content 
      continue 

     content_split = content.split() 

     if i == l: 
      r[k] = getattr(Convert(*content_split), k)() 
     else: 
      next_k = content_split.pop() 

     r[k] = getattr(Convert(*content_split), k)() 

     k = next_k 

    return r 


if __name__ == "__main__": 
    print line_to_dict('time=2016/06/14 16:44:00.000 level=1 sequenceNum=35 user=Username message=This is a message') 
関連する問題