Divisione di una stringa separata da punto e virgola in un dizionario, in Python


86

Ho una stringa che assomiglia a questa:

"Name1=Value1;Name2=Value2;Name3=Value3"

C'è una classe / funzione incorporata in Python che prenderà quella stringa e costruirà un dizionario, come se avessi fatto questo:

dict = {
    "Name1": "Value1",
    "Name2": "Value2",
    "Name3": "Value3"
}

Ho esaminato i moduli disponibili ma non riesco a trovare nulla che corrisponda.


Grazie, so come creare il codice pertinente da solo, ma poiché soluzioni così piccole di solito sono campi minati in attesa di accadere (ad es. Qualcuno scrive: Name1 = 'Value1 = 2';) ecc., Di solito preferisco alcuni pre- funzione testata.

Allora lo farò io.


la tua domanda richiede il supporto s = r'Name1='Value=2';Name2=Value2;Name3=Value3;Name4="Va\"lue;\n3"'dell'input (nota: un punto e virgola all'interno di una stringa tra virgolette, una virgoletta viene preceduta da una barra rovesciata, \nviene utilizzato un escape , vengono utilizzate sia virgolette singole che doppie)?
jfs

Questa mia domanda ha più di 6 anni, il codice che lo coinvolge è stato sostituito da tempo :) E no, non richiedeva supporto per le citazioni. Volevo solo avere una funzione predefinita invece di scrivere qualcosa da solo. Tuttavia, il codice è sparito da tempo.
Lasse V. Karlsen

Risposte:


145

Non ci sono incorporati, ma puoi farlo abbastanza semplicemente con una comprensione del generatore:

s= "Name1=Value1;Name2=Value2;Name3=Value3"
dict(item.split("=") for item in s.split(";"))

[Modifica] Dal tuo aggiornamento indichi che potresti dover gestire le quotazioni. Questo complica le cose, a seconda del formato esatto che stai cercando (quali caratteri di citazione sono accettati, quali caratteri di escape, ecc.). Potresti voler guardare il modulo csv per vedere se può coprire il tuo formato. Ecco un esempio: (nota che l'API è un po 'goffa per questo esempio, poiché CSV è progettato per iterare attraverso una sequenza di record, quindi le chiamate .next () che sto facendo per guardare solo la prima riga. adatto alle tue esigenze):

>>> s = "Name1='Value=2';Name2=Value2;Name3=Value3"

>>> dict(csv.reader([item], delimiter='=', quotechar="'").next() 
         for item in csv.reader([s], delimiter=';', quotechar="'").next())

{'Name2': 'Value2', 'Name3': 'Value3', 'Name1': 'Value1=2'}

A seconda dell'esatta struttura del formato, potrebbe essere necessario scrivere il proprio semplice parser.


il codice non gestisce s = "Name1='Value;2';Name2=Value2;Name3=Value3"le virgolette , prova: (nota: punto e virgola nel Name1valore tra virgolette ).
jfs

1
Non ho idea del motivo per cui il secondo esempio getta AttributeError: '_csv.reader' object has no attribute 'next'per me. Certo che l'ho fatto import csv.
Youngjae

@Brian Esiste un modo per memorizzare i valori come numero intero anziché come stringa?
ChasedByDeath

come si può fare il contrario @Brain
Jamil Noyda

6

Questo si avvicina a fare quello che volevi:

>>> import urlparse
>>> urlparse.parse_qs("Name1=Value1;Name2=Value2;Name3=Value3")
{'Name2': ['Value2'], 'Name3': ['Value3'], 'Name1': ['Value1']}

2
si rompe se c'è &o %nell'ingresso.
jfs

@jfs ma la stringa non contiene nessuno di questi.
Vishal Singh

@VishalSingh: la maggior parte dei visitatori su StackOverflow proviene da Google e quindi le risposte qui non sono solo per il poster originale che ha posto la domanda. Se venissi qui alla ricerca di come analizzare una "stringa separata da punto e virgola in un dizionario, in Python", le mie stringhe potrebbero contenere &o %- per lo meno, vale la pena ricordare che la risposta non funziona per tali stringhe.
jfs

4
s1 = "Name1=Value1;Name2=Value2;Name3=Value3"

dict(map(lambda x: x.split('='), s1.split(';')))

1

Può essere fatto semplicemente mediante l'unione di stringhe e la comprensione della lista

",".join(["%s=%s" % x for x in d.items()])

>>d = {'a':1, 'b':2}
>>','.join(['%s=%s'%x for x in d.items()])
>>'a=1,b=2'

-2
easytiger $ cat test.out test.py | sed 's/^/    /'
p_easytiger_quoting:1.84563302994
{'Name2': 'Value2', 'Name3': 'Value3', 'Name1': 'Value1'}
p_brian:2.30507516861
{'Name2': 'Value2', 'Name3': "'Value3'", 'Name1': 'Value1'}
p_kyle:7.22536420822
{'Name2': ['Value2'], 'Name3': ["'Value3'"], 'Name1': ['Value1']}
import timeit
import urlparse

s = "Name1=Value1;Name2=Value2;Name3='Value3'"

def p_easytiger_quoting(s):
    d = {}
    s = s.replace("'", "")
    for x in s.split(';'):
        k, v = x.split('=')
        d[k] = v
    return d


def p_brian(s):
    return dict(item.split("=") for item in s.split(";"))

def p_kyle(s):
    return urlparse.parse_qs(s)



print "p_easytiger_quoting:" + str(timeit.timeit(lambda: p_easytiger_quoting(s)))
print p_easytiger_quoting(s)


print "p_brian:" + str(timeit.timeit(lambda: p_brian(s)))
print p_brian(s)

print "p_kyle:" + str(timeit.timeit(lambda: p_kyle(s)))
print p_kyle(s)

Questo non risponde alla domanda, perché non gestisce le citazioni. Prova s = "Name1='Value1=2';Name2=Value2" and csv` (come nella risposta accettata di Brian) o parse_qs(come in Kyle) lo farà bene, mentre il tuo solleverà a ValueError. L'OP dice specificamente che "soluzioni così piccole di solito sono campi minati in attesa di essere implementati", motivo per cui vuole una soluzione integrata o un'altra soluzione ben testata, e fornisce un esempio che interromperà il codice.
abarnert

Ahh non l'ho visto. ancora. sarebbe comunque più veloce di tutte le tue soluzioni pre-leggere quelle nella stringa principale prima che l'iterazione abbia luogo e richiamando la funzione di sostituzione migliaia di volte. Aggiornerò
easytiger

Non sono sicuro di come lo preparerai. Ma anche se lo fai, questo sembra esattamente ciò di cui l'OP aveva paura in una soluzione semplice. Sei sicuro che non ci siano altre mine davanti? Puoi dimostrarlo con soddisfazione dell'OP?
abarnert

OK, ora che ho visto la tua modifica ... Primo, s.replacenon fa niente; restituisce solo una nuova stringa che ignori. Secondo, anche se hai indovinato ( s = s.replace…), questo non risolve il problema, ne aggiunge solo uno nuovo. Provalo sul mio esempio o sull'OP.
abarnert

La specifica comprende chiaramente la manipolazione del input di esempio ha citato nella sua interrogazione, Name='Value1=2';. E il tuo codice non lo gestisce. E non sono sicuro di come lo disinfetteresti senza analizzarlo in qualche modo che sarà altrettanto lento come urlparseo csvin primo luogo.
abarnert

-2

SE Value1, Value2 sono solo segnaposto per valori effettivi, puoi anche utilizzare la dict()funzione in combinazione con eval().

>>> s= "Name1=1;Name2=2;Name3='string'"
>>> print eval('dict('+s.replace(';',',')+')')
{'Name2: 2, 'Name3': 'string', 'Name1': 1}

Questo perché la dict()funzione comprende la sintassi dict(Name1=1, Name2=2,Name3='string'). Gli spazi nella stringa (ad es. Dopo ogni punto e virgola) vengono ignorati. Ma nota che i valori della stringa richiedono la citazione.


Grazie, upvote string.replace ha funzionato bene. Non so perché non ho potuto dividere. Ho fatto i = textcontrol.GetValue () su tc box, quindi o = i.split (';') ma non ho emesso una stringa che si è solo lamentata del formato, a differenza di replace.
Iancovici

1
s.replace(';'-soluzione basata si interrompe se ;all'interno di un valore tra virgolette. eval è malvagio e non è necessario in questo caso.
jfs
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.