Dividir una cadena separada por punto y coma en un diccionario, en Python

84

Tengo una cadena que se parece a esto:

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

¿Hay una clase / función incorporada en Python que tomará esa cadena y construirá un diccionario, como si hubiera hecho esto?

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

He revisado los módulos disponibles pero parece que no puedo encontrar nada que coincida.


Gracias, sé cómo hacer el código relevante yo mismo, pero dado que estas soluciones tan pequeñas suelen ser campos míos esperando a suceder (es decir, alguien escribe: Name1 = 'Value1 = 2';) etc., entonces generalmente prefiero algunas pre función probada.

Entonces lo haré yo mismo.

Lasse V. Karlsen
fuente
¿Su pregunta requiere que respalde la s = r'Name1='Value=2';Name2=Value2;Name3=Value3;Name4="Va\"lue;\n3"'entrada (nota: un punto y coma dentro de una cadena entre comillas, una cita se escapa usando una barra invertida, \nse usa escape , se usan comillas simples y dobles)?
jfs
Esta pregunta mía tiene más de 6 años, el código que involucraba esto ha sido reemplazado hace mucho tiempo :) Y no, no requería soporte para citas. Solo quería tener una función prediseñada en lugar de escribir algo yo mismo. Sin embargo, el código desapareció hace mucho tiempo.
Lasse V. Karlsen

Respuestas:

144

No hay una función incorporada, pero puede lograr esto de manera bastante simple con una comprensión del generador:

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

[Editar] Desde su actualización, indica que es posible que deba manejar las cotizaciones. Esto complica las cosas, dependiendo del formato exacto que esté buscando (qué caracteres de comillas se aceptan, qué caracteres de escape, etc.). Es posible que desee ver el módulo csv para ver si puede cubrir su formato. Aquí hay un ejemplo: (Tenga en cuenta que la API es un poco torpe para este ejemplo, ya que CSV está diseñado para iterar a través de una secuencia de registros, de ahí las llamadas .next () que estoy haciendo para mirar solo la primera línea. Ajustar a se adapte a sus necesidades):

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

Sin embargo, dependiendo de la estructura exacta de su formato, es posible que deba escribir su propio analizador simple.

Brian
fuente
el código no maneja s = "Name1='Value;2';Name2=Value2;Name3=Value3"las comillas , intente: (nota: punto y coma en el Name1valor entre comillas ).
jfs
1
No tengo idea de por qué el segundo ejemplo AttributeError: '_csv.reader' object has no attribute 'next'me arroja . Por supuesto que lo hice import csv.
Youngjae
@Brian ¿Hay alguna forma de almacenar los valores como enteros en lugar de cadenas?
ChasedByDeath
6

Esto se acerca a hacer lo que querías:

>>> import urlparse
>>> urlparse.parse_qs("Name1=Value1;Name2=Value2;Name3=Value3")
{'Name2': ['Value2'], 'Name3': ['Value3'], 'Name1': ['Value1']}
Kyle Gibson
fuente
2
se rompe si hay &o %en la entrada.
jfs
@jfs pero la cadena no contiene ninguno de esos.
Vishal Singh
@VishalSingh: la mayoría de los visitantes de StackOverflow son de Google y, por lo tanto, las respuestas aquí no son solo para el autor original que hizo la pregunta. Si vine aquí buscando cómo analizar una "cadena separada por punto y coma en un diccionario, en Python", entonces mis cadenas podrían contener &o %, al menos, vale la pena mencionar que la respuesta no funciona para tales cadenas.
jfs
3
s1 = "Name1=Value1;Name2=Value2;Name3=Value3"

dict(map(lambda x: x.split('='), s1.split(';')))
D. Om
fuente
1

Se puede hacer simplemente mediante combinación de cadenas y comprensión de listas

",".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'
vijay
fuente
-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)
tranquilo tigre
fuente
Esto no responde a la pregunta, porque no maneja las citas. Pruebe s = "Name1='Value1=2';Name2=Value2" and csv` (como en la respuesta aceptada de Brian) o parse_qs(como en la de Kyle) lo hará bien, mientras que el suyo generará un ValueError. El OP dice específicamente que "estas soluciones tan pequeñas suelen ser campos de minas esperando a que ocurran", por lo que quiere una solución incorporada u otra solución bien probada, y da un ejemplo que romperá su código.
abarnert
Ahh, no vi eso. todavía. aún sería más rápido que todas sus soluciones preparar las de la cadena principal antes de que tenga lugar la iteración y recuperar la función de reemplazo miles de veces. Actualizaré
easytiger
No estoy seguro de cómo lo vas a preparar. Pero incluso si lo hace, esto parece exactamente lo que temía el OP en una solución simple. ¿Estás seguro de que no hay otras minas más adelante? ¿Puede probarlo a satisfacción del OP?
abarnert
Bien, ahora que he visto tu edición ... Primero, s.replaceno hace nada en absoluto; simplemente devuelve una nueva cadena que ignora. En segundo lugar, incluso si lo hizo bien ( s = s.replace…), eso no soluciona el problema, solo agrega uno nuevo encima. Pruébelo en mi ejemplo o en los OP.
abarnert
La especificación claramente incluye el manejo de la entrada de muestra que mencionó en su pregunta Name='Value1=2';. Y su código no lo maneja. Y no estoy seguro de cómo desinfectaría eso sin analizarlo de alguna manera que sea tan lento como urlparseo csven primer lugar.
abarnert
-2

SI su Valor1, Valor2 son solo marcadores de posición para valores reales, también puede usar la dict()función en combinación con eval().

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

Esto se debe a que la dict()función comprende la sintaxis dict(Name1=1, Name2=2,Name3='string'). Los espacios en la cadena (por ejemplo, después de cada punto y coma) se ignoran. Pero tenga en cuenta que los valores de cadena requieren comillas.

Rabarberski
fuente
Gracias, upvote string.replace funcionó bien. No sé por qué no pude separarme. Hice i = textcontrol.GetValue () en el cuadro tc, luego o = i.split (';') pero no emití una cadena, solo se quejó del formato, a diferencia de reemplazar.
Iancovici
1
s.replace(';'-basada solución se rompe si hay ;dentro de un valor cotizado. eval es malo y no es necesario en este caso.
jfs