2015-05-28 9 views
12

Jaki jest najlepszy sposób, aby zbudować słownik z ciągiem jak poniżej:Włączanie ciąg ze wspornikami osadzonych w słowniku

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

Więc kluczem jest zawsze ciągiem znaków bez spacji, ale wartość jest albo ciąg lub ciąg w nawiasach klamrowych (ma spacje)?

Jak byś dict go do:

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

Jak definiujesz "najlepszy sposób"? Szybka, elegancka, łatwa do utrzymania, ...? Co ty wypróbowałeś sam? Co zadziałało, a co nie? Dlaczego nie? –

Odpowiedz

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

Można spróbować.

wyjściowa:

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

Tutaj z re.findall wydobywamy key i jego value. re.findall zwraca listę z krotkami wszystkich kluczy, par wartości. Używanie dict na liście krotek daje ostateczną odpowiedź. Read more here.

+2

To jest naprawdę świetne! Byłoby wspaniale, gdybyś mógł zamieścić trochę wyjaśnienia, abym mógł się lepiej nauczyć i zrozumieć. Dzięki. –

+0

@vks To świetnie! W jaki sposób zaktualizowałbym to do obsługi przypadków ze spacjami w nawiasach takich jak to: "{key1 value1} {key2 value2} {key3 {value with spaces}}" – mtmt

+0

@mtmt 'print dict (re.findall (r" \ {\ s * (\ S +) \ s + \ {* (. *?) \} + ", X))' – vks

2

Zakładając, że nie masz nic w ciąg bardziej niż to, co jest zagnieżdżony w swoim przykładzie, można najpierw użyć uprzedzona/lookbehind twierdzeń podzielić ciąg na swoich par klucz-wartość, patrząc na wzór } { (koniec jednej pary wsporników i na początku drugiego.)

>>> str = '{key1 value1} {key2 value2} {key3 {value with spaces}}' 
>>> pairs = re.split('(?<=})\s*(?={)', str) 

To mówi „spotkanie na każdym \s* (spacji), który ma } przed nim i { po nim, ale nie obejmują tych wsporników w samym meczu. "

Wtedy masz swoje pary klucz-wartość:

>>> pairs 
['{key1 value1}', '{key2 value2}', '{key3 {value with spaces}}'] 

które można podzielić na białe znaki z parametrem maxsplit ustawiony na 1, aby upewnić się, że dzieli się tylko na pierwszym miejscu. W tym przykładzie użyłem także indeksowania ciągów ([1:-1]), aby pozbyć się nawiasów klamrowych, które znam na początku i na końcu każdej pary.

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

potem po prostu sprawdzić, czy wartość jest ujęty w nawiasy klamrowe i usunąć je, jeśli trzeba przed włożeniem ich do swojego słownika.

Jeśli jest zagwarantowane, że pary klucz/wartość będą zawsze oddzielone pojedynczym znakiem spacji, można zamiast tego użyć zwykłego starego ciągu znaków.

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

nie mogę uczynić go bardziej elegancko:

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] 

its bardzo hacky, ale powinno załatwić sprawę.

+0

Uważa się, że trzecią wartością jest lista wartości. Odpowiedź vks jest w każdym razie znacznie lepsza. – Renatius

+2

To może być hacky, ale wydaje się unikać jawnego wyrażeń regularnych, co może być korzystne w niektórych przypadkach. – Pureferret

1

Odpowiedź @vks nie sprawdza zrównoważonych nawiasów klamrowych.Spróbuj wykonać następujące czynności:

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

spróbować zamiast:

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

to znaczy, że pasuje tylko na części z prawidłowego stężenia.

(?P<Brace>\{) zapisuje mecz o {, a później (?(Brace)\}) dopasuje } tylko jeśli pierwszy dopasowany, a więc muszą pochodzić w szelki dopasowane parami. I przez konstrukt (?(Brace)...|...), jeśli pasuje do siebie, część wartości może zawierać cokolwiek z wyjątkiem nawiasów klamrowych ([^{}]*), w przeciwnym razie spacja nie jest dozwolona ([^{}\s]*).

Ponieważ opcjonalny nawias klamrowy jest dopasowywany w regexp, a więc zwracany na liście, musimy wyodrębnić elementy 0 i 2 z każdej listy przez funkcję map().

Regeksy łatwo się brudzą.