Включение строки со встроенными скобками в словарь

Какой лучший способ построить словарь из строки, такой как ниже:

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

Итак, ключ всегда является строкой без пробелов, но это значение является строкой или строкой в ​​фигурных скобках (у нее есть пробелы)?

Как бы вы это определили:

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

Ответ 1

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 и его value. re.findall возвращает список с кортежами всех пар ключей, значений. Использование dict в списке кортежей дает окончательный ответ. Подробнее читайте здесь.

Ответ 2

Я не могу сделать это более элегантно:

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]

Это очень хаки, но он должен выполнять эту работу.

Ответ 3

Предполагая, что в вашей строке нет ничего более вложенного, чем то, что в вашем примере, вы можете сначала использовать утверждения lookahead/lookbehind, чтобы разделить строку на пары с ключом, ища шаблон } { ( конец одной пары скобок и начало другого.)

>>> 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}']

Ответ 4

Ответ @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]*).

Поскольку необязательная скобка сопоставляется в regexp и, таким образом, возвращается в список, нам нужно извлечь элементы 0 и 2 из каждого списка с помощью функции map().

Regexps легко становится беспорядочным.