将字典的字符串表示形式转换为字典?


767

如何将a的str表示形式(dict例如以下字符串)转换为a dict

s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"

我宁愿不使用eval。我还能使用什么?

这样做的主要原因是他写的我的同事课程之一,将所有输入都转换为字符串。我不打算去修改他的课程,以解决这个问题。


1
如果您不能使用Python 2.6,则可以使用简单的安全性实现,例如code.activestate.com/recipes/364469。它搭载在Python编译器上,因此您不必自己做所有繁琐的工作。
Ned Batchelder

11
注意:对于那些看似相似的 JSON数据的用户,您想改为使用Python读取Parse JSON。JSON 与Python不同。如果您"的字符串两端有双引号,则可能是JSON数据。您也可以找nulltrue或者false,Python语法的使用NoneTrueFalse
马丁·彼得斯

Answers:


1167

从Python 2.6开始,您可以使用内置的ast.literal_eval

>>> import ast
>>> ast.literal_eval("{'muffin' : 'lolz', 'foo' : 'kitty'}")
{'muffin': 'lolz', 'foo': 'kitty'}

这比使用更为安全eval。正如其文档所说:

>>>帮助(ast.literal_eval)
帮助ast模块中的literal_eval函数:

literal_eval(node_or_string)
    安全地评估表达式节点或包含Python的字符串
    表达。提供的字符串或节点只能由以下内容组成
    Python文字结构:字符串,数字,元组,列表,字典,布尔值,
    和没有。

例如:

>>> eval("shutil.rmtree('mongo')")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
  File "/opt/Python-2.6.1/lib/python2.6/shutil.py", line 208, in rmtree
    onerror(os.listdir, path, sys.exc_info())
  File "/opt/Python-2.6.1/lib/python2.6/shutil.py", line 206, in rmtree
    names = os.listdir(path)
OSError: [Errno 2] No such file or directory: 'mongo'
>>> ast.literal_eval("shutil.rmtree('mongo')")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/Python-2.6.1/lib/python2.6/ast.py", line 68, in literal_eval
    return _convert(node_or_string)
  File "/opt/Python-2.6.1/lib/python2.6/ast.py", line 67, in _convert
    raise ValueError('malformed string')
ValueError: malformed string

我应该补充一点,您需要清理用于ast.literal_eval的字符串。(确保字符串中的引号/双引号都被转义)
Paulo Matos

我收到此错误,我在Windows 7 x64上的python 2.6(x86)上,文件“ D:\ Python26 \ lib \ ast.py”,第48行,在literal_eval中,node_or_string = parse(node_or_string,mode ='eval')文件“ D :\ Python26 \ lib \ ast.py“,第36行,在解析中返回编译(expr,文件名,模式,PyCF_ONLY_AST)文件“ <unknown>”,第1行^ SyntaxError:语法无效

什么"dict(a=1)"风格的字符串?
n611x007 2014年

这似乎不适用于字典中的枚举值。例如:d =“ {'col':<Colors.RED:2>,'val':2}”
shivshnkr

3
为什么不使用json.dumps和json.loads INSEAD,我发现这个解决方案的更多elevant吴丹使用eval
Auros132

232

https://docs.python.org/3.8/library/json.html

JSON可以解决此问题,尽管其解码器希望在键和值周围使用双引号。如果您不介意更换骇客...

import json
s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
json_acceptable_string = s.replace("'", "\"")
d = json.loads(json_acceptable_string)
# d = {u'muffin': u'lolz', u'foo': u'kitty'}

请注意,如果将单引号作为键或值的一部分,则由于字符替换不当而导致此操作失败。仅当您对评估解决方案强烈反对时,才建议使用此解决方案。

有关JSON单引号的更多信息:jQuery.parseJSON由于JSON中的单引号已转义而引发“无效JSON”错误


12
{"foo": "b'ar"}
Mark E. Haase 2015年

4
{'foo': (1, 2, 3)}
Mark E. Haase 2015年

1
我一直在寻找这种解决方案。 +1用于通知解码器希望在键和值周围使用双引号。
h8pathak '02

另一个问题是"{0: 'Hello'}"
FinnÅrupNielsen

3
如果尾随逗号(不兼容JSON),也将失败,例如:“ {'muffin':'lolz','foo':'kitty',}”
guival,

159

使用json.loads

>>> import json
>>> h = '{"foo":"bar", "foo2":"bar2"}'
>>> d = json.loads(h)
>>> d
{u'foo': u'bar', u'foo2': u'bar2'}
>>> type(d)
<type 'dict'>

13
我认为它不能回答OP的回答。我们如何使用json.laads将字符串s =“ {'muffin':'lolz','foo':'kitty'}”转换为dict?
technazi

为什么在输出中打印'u'?例如-str ='{“ 1”:“ P”,“ 2”:“ N”,“ 3”:“ M”}'d = json.loads(str)print d输出为:{u'1': u'P',u'3':u'M',u'2':u'N'}
905 2016年

2
@technazi:json.loads(h.replace(“'”,'“'))
ntg

但是,有一些限制,例如:h ='{“ muffin”:“ lolz”,“ foo”:“ kitty”,}',h ='{“ muffin's”:“ lolz”,“ foo”:“ kitty “}”,(只是在类似答案中注意到了相同评论的一部分...为了完整
起见,

4
在我看来,这是最短,最简单的方法……绝对是我个人所喜欢的方法。
nostradamus

35

以OP为例:

s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"

我们可以使用Yaml处理字符串中的这种非标准json:

>>> import yaml
>>> s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> s
"{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> yaml.load(s)
{'muffin': 'lolz', 'foo': 'kitty'}

1
这将导致“是”和“否”字符串转换为True / False
Eric Marcos

23

如果始终可以信任该字符串,则可以使用eval(或literal_eval按建议使用;无论该字符串是什么都是安全的。)否则,您需要一个解析器。如果JSON解析器(例如simplejson)仅存储符合JSON方案的内容,则该解析器将起作用。


8
从2.6开始,simplejson作为json模块包含在Python标准库中。
Eli Courtwright 09年

11
是的,这是一个很好的答案,但是请注意,正式的JSON不支持单引号的字符串,如原始发布者的示例所示。
本·霍伊特,

19

使用json。该ast库消耗大量内存,并且速度较慢。我有一个过程需要读取156Mb的文本文件。Ast转换字典需要5分钟的延迟,json而使用内存减少60%则需要1分钟!


13
但有其局限性:尝试转换字符串“ {'foo':'bar',}”
ntg

12

总结一下:

import ast, yaml, json, timeit

descs=['short string','long string']
strings=['{"809001":2,"848545":2,"565828":1}','{"2979":1,"30581":1,"7296":1,"127256":1,"18803":2,"41619":1,"41312":1,"16837":1,"7253":1,"70075":1,"3453":1,"4126":1,"23599":1,"11465":3,"19172":1,"4019":1,"4775":1,"64225":1,"3235":2,"15593":1,"7528":1,"176840":1,"40022":1,"152854":1,"9878":1,"16156":1,"6512":1,"4138":1,"11090":1,"12259":1,"4934":1,"65581":1,"9747":2,"18290":1,"107981":1,"459762":1,"23177":1,"23246":1,"3591":1,"3671":1,"5767":1,"3930":1,"89507":2,"19293":1,"92797":1,"32444":2,"70089":1,"46549":1,"30988":1,"4613":1,"14042":1,"26298":1,"222972":1,"2982":1,"3932":1,"11134":1,"3084":1,"6516":1,"486617":1,"14475":2,"2127":1,"51359":1,"2662":1,"4121":1,"53848":2,"552967":1,"204081":1,"5675":2,"32433":1,"92448":1}']
funcs=[json.loads,eval,ast.literal_eval,yaml.load]

for  desc,string in zip(descs,strings):
    print('***',desc,'***')
    print('')
    for  func in funcs:
        print(func.__module__+' '+func.__name__+':')
        %timeit func(string)        
    print('')

结果:

*** short string ***

json loads:
4.47 µs ± 33.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
builtins eval:
24.1 µs ± 163 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
ast literal_eval:
30.4 µs ± 299 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
yaml load:
504 µs ± 1.29 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

*** long string ***

json loads:
29.6 µs ± 230 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
builtins eval:
219 µs ± 3.92 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
ast literal_eval:
331 µs ± 1.89 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
yaml load:
9.02 ms ± 92.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

结论:更喜欢json.loads


5
除此之外,这对他的单引号字符串无效,这是他最初遇到的问题的一部分。性能从未被提及。
Michael Campbell

1
哇...。超级解说....
点点樱桃

5
string = "{'server1':'value','server2':'value'}"

#Now removing { and }
s = string.replace("{" ,"")
finalstring = s.replace("}" , "")

#Splitting the string based on , we get key value pairs
list = finalstring.split(",")

dictionary ={}
for i in list:
    #Get Key Value pairs separately to store in dictionary
    keyvalue = i.split(":")

    #Replacing the single quotes in the leading.
    m= keyvalue[0].strip('\'')
    m = m.replace("\"", "")
    dictionary[m] = keyvalue[1].strip('"\'')

print dictionary

3
这种方法有很多错误。如果一个键的值包含{}。如果它是嵌套的怎么办dict。如果值包含,?? 怎么办?

4

没有使用任何库:

dict_format_string = "{'1':'one', '2' : 'two'}"
d = {}
elems  = filter(str.isalnum,dict_format_string.split("'"))
values = elems[1::2]
keys   = elems[0::2]
d.update(zip(keys,values))

注意:由于已进行硬编码,split("'")因此仅适用于“单引号”数据的字符串。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.