Включение строки со встроенными скобками в словарь
Какой лучший способ построить словарь из строки, такой как ниже:
"{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 легко становится беспорядочным.