我有一个字符串列表,这些字符串被格式化为键/值对,用空格分隔。例如,消息可能是:
"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'}
有几点需要注意:
level
和sequenceNum
是数字,而不是字符串'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'])
我想知道是否可以在另一个方向上这样做。
答案 0 :(得分:2)
为此我会使用正则表达式。这可能不是最快(性能方面)或最简单(理解)的解决方案,但它肯定会有效。 (并且可能是你最接近“反向格式”)
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)
要将数字作为数字,您可以执行result['level'] = int(result['level'])
。
如果您感兴趣,我可以扩展一下我如何构建正则表达式以及如何改进它。
编辑:更改了表达式,以涵盖message=
在主题中的边缘情况。
答案 1 :(得分: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)
如果您的输入数据包含以下内容:"subject=message=subject=message=subject"
,则表示您遇到问题,因为它不明确。您必须清理输入,或者只是引发异常。
if data.count('=') != 6:
raise ValidationError('malformed input data: {}'.format(data))
答案 2 :(得分:0)
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)
答案 3 :(得分: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'}
您仍然需要转换数字,并从表的最后一行恢复消息的最后一个字。我可以在线进行这些操作,但认为他们会稍微阻塞演示文稿。
答案 4 :(得分: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')