Fractionner une chaîne séparée par des points-virgules en un dictionnaire, en Python


84

J'ai une chaîne qui ressemble à ceci:

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

Existe-t-il une classe / fonction intégrée en Python qui prendra cette chaîne et construira un dictionnaire, comme si j'avais fait ceci:

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

J'ai parcouru les modules disponibles mais je n'arrive pas à trouver quoi que ce soit qui corresponde.


Merci, je sais comment créer moi-même le code pertinent, mais comme ces petites solutions sont généralement des champs de mines qui attendent d'arriver (c'est-à-dire que quelqu'un écrit: Name1 = 'Value1 = 2';) etc. alors je préfère généralement des pré- fonction testée.

Je vais le faire moi-même alors.


votre question nécessite-t-elle de prendre en charge l' s = r'Name1='Value=2';Name2=Value2;Name3=Value3;Name4="Va\"lue;\n3"'entrée (note: un point-virgule dans une chaîne entre guillemets, un guillemet est échappé à l'aide d'une barre oblique inverse, un \néchappement est utilisé, des guillemets simples et doubles sont utilisés)?
jfs

Cette question a plus de 6 ans, le code qui impliquait cela a depuis longtemps été remplacé :) Et non, il ne nécessitait pas de support pour les citations. Je voulais juste avoir une fonction prédéfinie au lieu d'écrire quelque chose moi-même. Cependant, le code a disparu depuis longtemps.
Lasse V. Karlsen

Réponses:


144

Il n'y a pas de builtin, mais vous pouvez accomplir cela assez simplement avec une compréhension du générateur:

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

[Modifier] À partir de votre mise à jour, vous indiquez que vous devrez peut-être gérer les devis. Cela complique les choses, selon le format exact que vous recherchez (quels caractères de citation sont acceptés, quels caractères d'échappement, etc.). Vous voudrez peut-être regarder le module csv pour voir s'il peut couvrir votre format. Voici un exemple: (Notez que l'API est un peu maladroite pour cet exemple, car CSV est conçu pour parcourir une séquence d'enregistrements, d'où les appels .next () que je fais pour regarder simplement la première ligne. répondre à vos besoins):

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

En fonction de la structure exacte de votre format, vous devrez peut-être écrire votre propre analyseur simple.


le code ne gère pas les guillemets, essayez: s = "Name1='Value;2';Name2=Value2;Name3=Value3"(note: point-virgule dans la Name1valeur citée ).
jfs

1
Je ne sais pas pourquoi le deuxième exemple AttributeError: '_csv.reader' object has no attribute 'next'me jette . Bien sûr que je l'ai fait import csv.
Youngjae

@Brian Existe-t-il un moyen de stocker les valeurs sous forme d'entier plutôt que de chaîne?
ChasedByDeath le

6

Cela revient à faire ce que vous vouliez:

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

2
il casse s'il y a &ou %dans l'entrée.
jfs

@jfs mais la chaîne ne contient ni l'un ni l'autre.
Vishal Singh

@VishalSingh: la plupart des visiteurs de StackOverflow viennent de Google et les réponses ici ne sont donc pas réservées à l'affiche d'origine qui a posé la question. Si je suis venu ici pour chercher comment analyser une "chaîne séparée par des points-virgules dans un dictionnaire, en Python" alors mes chaînes pourraient contenir &ou %- à tout le moins, il convient de mentionner que la réponse ne fonctionne pas pour de telles chaînes.
jfs

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

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

1

Cela peut être simplement fait par jointure de chaîne et compréhension de liste

",".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)

Cela ne répond pas à la question, car il ne gère pas les citations. Essayez s = "Name1='Value1=2';Name2=Value2" and csv` (comme dans la réponse acceptée de Brian) ou parse_qs(comme dans Kyle) le fera correctement, tandis que le vôtre augmentera un ValueError. L'OP dit spécifiquement que "ces petites solutions sont généralement des champs de mines qui attendent de se produire", c'est pourquoi il veut une solution intégrée ou une autre solution bien testée, et il donne un exemple qui cassera votre code.
abarnert

Ahh je n'ai pas vu ça. encore. il serait toujours plus rapide que toutes vos solutions de préparer celles de la chaîne principale avant que l'itération n'ait lieu et de rappeler la fonction de remplacement des milliers de fois. Je vais mettre à jour
easytiger

Je ne sais pas comment vous allez le préparer. Mais même si vous le faites, cela semble être exactement ce dont l'OP avait peur dans une solution simple. Êtes-vous sûr qu'il n'y a pas d'autres mines à venir? Pouvez-vous le prouver à la satisfaction du PO?
abarnert

OK, maintenant que j'ai vu votre montage… Premièrement, s.replacene fait rien du tout; il renvoie simplement une nouvelle chaîne que vous ignorez. Deuxièmement, même si vous avez bien compris ( s = s.replace…), cela ne résout pas le problème, cela en ajoute simplement un nouveau par-dessus. Essayez-le sur mon exemple ou sur les OP.
abarnert

Le cahier des charges inclut clairement le traitement de l’échantillon d’entrée qu’il a mentionné dans sa question Name='Value1=2';. Et votre code ne le gère pas. Et je ne sais pas comment vous désinfecteriez cela sans l'analyser d'une manière qui sera tout aussi lente urlparseou csven premier lieu.
abarnert

-2

SI vos Value1, Value2 ne sont que des espaces réservés pour les valeurs réelles, vous pouvez également utiliser la dict()fonction en combinaison avec eval().

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

C'est parce que la dict()fonction comprend la syntaxe dict(Name1=1, Name2=2,Name3='string'). Les espaces dans la chaîne (par exemple après chaque point-virgule) sont ignorés. Mais notez que les valeurs de chaîne nécessitent des guillemets.


Merci, upvote string.replace a bien fonctionné. Je ne sais pas pourquoi je n'ai pas pu me séparer. J'ai fait i = textcontrol.GetValue () sur tc box, puis o = i.split (';') mais je n'ai pas sorti une chaîne juste plaint du format, contrairement à replace.
Iancovici

1
s.replace(';'-solution basée sur les ruptures s'il y a à l' ;intérieur d'une valeur citée. eval est mal et il est inutile dans ce cas.
jfs
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.