将包含嵌套括号的字符串转换为字典

14

如何从类似下面的字符串构建字典是最好的方法:

"{key1 value1} {key2 value2} {key3 {value with spaces}}"

所以关键字始终是一个没有空格的字符串,但值可以是一个字符串或用大括号包含的字符串(它有空格)?

你要如何将它转换为字典形式:

{'key1': 'value1',   'key2': 'value2',   'key3': 'value with spaces'}

你如何定义“最佳方式”?快速、优雅、可维护等?此外,你自己尝试过什么?哪些有效了,哪些没有?为什么? - Thomas Weller
4个回答

19
import re
x="{key1 value1} {key2 value2} {key3 {value with spaces}}"
print dict(re.findall(r"\{(\S+)\s+\{*(.*?)\}+",x))

你可以尝试这个。

输出:

{'key3': 'value with spaces', 'key2': 'value2', 'key1': 'value1'}

使用re.findall,我们提取key及其valuere.findall 返回一个包含所有键值对元组的列表。将列表元组传递给dict函数即可得到最终答案。 在此处阅读更多信息。


2
这真的很棒!如果您能发布一点解释,那我就可以更好地学习和理解它。谢谢。 - Joe T. Boka
@vks 太好了!我该如何更新它以支持括号周围带有空格的情况,例如:"{ key1 value1 } { key2 value2 } { key3 {value with spaces} }"? - mtmt
@mtmt print dict(re.findall(r"\{\s*(\S+)\s+\{*(.*?)\}+",x)) 请输出 print dict(re.findall(r"\{\s*(\S+)\s+\{*(.*?)\}+",x)) - vks
实际上,对于有空格的情况,这将是一个简单的更改"{ (\S+)\s+{(.?)}+",我只需要想出一种支持两种情况的方法。 - mtmt
@mtmt 将其更改为\s*以支持两种情况。 - vks
如果字符串无效,您可以微调表达式以禁止回溯,这应该会带来性能上的好处。将前三个量词变为贪婪限定符:\{(\S++)\s++\{*+(.*?)\}+ - Falco

4

我不能再优雅地表达了:

input = "{key1 value1} {key2 value2} {key3 {value with spaces}}"
x = input.split("} {")             # creates list with keys and values
y = [i.split(" {") for i in y]     # separates the list-values from keys
# create final list with separated keys and values, removing brackets
z = [[i.translate(None,"{").translate(None,"}").split() for i in j] for j in y]

fin = {}
for i in z:
    fin[i[0][0]] = i[-1]

这很hacky,但应该能胜任工作。


虽然第三个值是一个值列表,但vks的答案更好。 - Renatius
2
这可能有些取巧,但似乎避免了过度使用正则表达式,在某些情况下可能会有益。 - AncientSwordRage

2
假设您的字符串中没有比示例更嵌套的内容,您可以首先使用前瞻/后顾断言将字符串拆分为键值对,查找模式} {(一对括号的结尾和另一对括号的开头)。
>>> str = '{key1 value1} {key2 value2} {key3 {value with spaces}}'
>>> pairs = re.split('(?<=})\s*(?={)', str)

这段文字的意思是:“匹配任何在之前有 } 且之后有 {\s*(空白字符),但不包括这些括号在匹配中。”
接下来是键值对:
>>> pairs
['{key1 value1}', '{key2 value2}', '{key3 {value with spaces}}']

可以使用maxsplit参数将字符串按空格分割,设置为1,则只在第一个空格处分割。在此示例中,我还使用了字符串索引([1:-1])来去掉我知道在每对的开头和结尾的花括号。
>>> simple = pairs[0] 
>>> complex = pairs[2]  
>>> simple
'{key1 value1}'
>>> complex
'{key3 {value with spaces}}'
>>> simple[1:-1]
'key1 value1'
>>> kv = re.split('\s+', simple[1:-1], maxsplit=1)
>>> kv
['key1', 'value1']
>>> kv3 = re.split('\s+', complex[1:-1], maxsplit=1)
>>> kv3
['key3', '{value with spaces}']

如果你需要将其放入字典中,那么请检查值是否被花括号包围,如果是的话,请先将其移除。

如果可以保证键/值对之间始终由单个空格字符分隔,则可以使用普通的字符串分割方法。

>>> kv3 = complex[1:-1].split(' ', maxsplit=1)
>>> kv3
['key3', '{value with spaces}']

1

@vks的答案没有检查平衡的大括号。请尝试以下方法:

>>> x="{key3 {value with spaces} {key4 value4}}"
>>> dict(re.findall(r"\{(\S+)\s+\{*(.*?)\}+",x))
{'key3': 'value with spaces', 'key4': 'value4'}

尝试使用以下内容:
>>> dict(map(lambda x:[x[0],x[2]], re.findall(r'\{(\S+)\s+(?P<Brace>\{)?((?(Brace)[^{}]*|[^{}\s]*))(?(Brace)\})\}',x)))
{'key4': 'value4'}

也就是说,它只匹配正确括号的部分。 (?P<Brace>\{)保存一个{的匹配,稍后的(?(Brace)\})只有在第一个匹配成功时才会匹配},因此大括号必须成对出现。通过(?(Brace)...|...)结构,如果\Brace匹配成功,则值部分可以包含除大括号之外的任何内容([^{}]*),否则不允许有空格([^{}\s]*)。
由于可选括号在正则表达式中匹配并返回到列表中,因此我们需要使用map()函数从每个列表中提取元素0和2。
正则表达式很容易变得混乱。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接