【问题标题】:Convert a String representation of a Dictionary to a dictionary?将字典的字符串表示形式转换为字典?
【发布时间】:2010-11-02 13:26:11
【问题描述】:

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

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

我不喜欢使用eval。我还能用什么?

主要原因是他编写的我的同事课程之一,将所有输入转换为字符串。我没心情去修改他的课程来处理这个问题。

【问题讨论】:

  • 如果您不能使用 Python 2.6,您可以使用简单的 safeeval 实现,例如 code.activestate.com/recipes/364469 它搭载在 Python 编译器上,因此您不必自己完成所有繁重的工作。跨度>
  • 注意:对于那些带有 看似相似的外观 JSON数据的人,你想去阅读@987654322 @ 反而。 JSON 与 Python 不同。如果您的字符串周围有" 双引号,则您可能有JSON 数据。也可以查找nulltruefalse,Python语法使用NoneTrueFalse

标签: python string dictionary


【解决方案1】:

如果字符串总是可以被信任,你可以使用eval(或者按照建议使用literal_eval;不管字符串是什么都是安全的。)否则你需要一个解析器。如果 JSON 解析器(例如 simplejson)只存储符合 JSON 方案的内容,那么它就可以工作。

【讨论】:

  • 从 2.6 开始,simplejson 作为 json 模块包含在 Python 标准库中。
  • 是的,这是一个很好的答案,但请注意官方 JSON 不支持单引号字符串,如原始海报示例中所述。
【解决方案2】:

你可以使用内置的ast.literal_eval

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

这比使用eval 更安全。正如它自己的文档所说:

>>> 帮助(ast.literal_eval) 关于 ast 模块中函数 literal_eval 的帮助: 文字评估(节点或字符串) 安全地评估表达式节点或包含 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 一起使用。 (确保字符串中的引号/双引号被转义)
  • 我收到此错误我在 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 行,解析中返回 compile(expr, filename, mode, PyCF_ONLY_AST) 文件“”,第 1 行 ^ SyntaxError: invalid syntax
  • "dict(a=1)" 样式字符串呢?
  • 这似乎不适用于字典中的枚举值。例如:d = "{'col': , 'val': 2}"
  • 为什么不使用 json.dumps 和 json.loads insead,我发现这个解决方案比使用 eval 更重要
【解决方案3】:

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

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

更多关于json单引号:jQuery.parseJSON throws “Invalid JSON” error due to escaped single quote in JSON

【讨论】:

  • 我一直在寻找这个解决方案。 +1 通知解码器需要在键和值周围加上双引号。
  • 另一个问题是"{0: 'Hello'}"
  • 如果您有尾随逗号(不符合 JSON 标准),这也会失败,例如:“{'muffin' : 'lolz', 'foo' : 'kitty',}”
  • 单引号字符串、元组文字和尾随逗号不是有效的 JSON。 json.loads 仅适用于有效的 JSON 字符串。请参阅此处的规范:json.org 使用 json.loads 是最安全的解决方案,因此请尽可能使用。如有必要,我建议将您的输入转换为有效的 JSON。
  • 如果您有 unicode 字符串,此解决方案也不起作用
【解决方案4】:

使用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'>

【讨论】:

  • 我认为它不能回答 OP 的答案。我们如何使用 json.laads 将字符串 s = "{'muffin' : 'lolz', 'foo' : 'kitty'}" 转换为 dict?
  • @technazi: json.loads(h.replace("'",'"'))
  • 但是有限制,例如:h= '{"muffin" : "lolz", "foo" : "kitty",}',还有 h= '{"muffin's" : "lolz ", "foo" : "kitty"}', (只是在类似的答案中注意到相同 cmets 的一部分......为了完整性仍然离开这里......)
  • 在我看来,这是最短和最简单的方法......绝对是我个人更喜欢的一种。
  • @nostradamus 异常、浮点值、元组等太多。
【解决方案5】:

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

【讨论】:

  • 但有其局限性:尝试转换字符串 "{'foo':'bar',}"
【解决方案6】:

以 OP 为例:

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

我们可以使用Yaml来处理这种非标准的json in string:

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

【讨论】:

  • 这将导致'yes'和'no'字符串被转换为True / False
  • 我的值正常工作....但我收到一个错误“AMLLoadWarning:不推荐在没有 Loader=... 的情况下调用 yaml.load(),因为默认的 Loader 不安全.请阅读msg.pyyaml.org/load了解详细信息。”这是什么??
  • 仅将此 yaml 解析器用于 trusted 输入。最好使用safe_load 以避免安全隐患。
【解决方案7】:
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

【讨论】:

  • 这种方法有很多错误。如果键的值包含{} 怎么办。如果它是嵌套的dict。如果值包含, 怎么办??
【解决方案8】:

没有使用任何库(python2):

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("'") 仅适用于数据是“单引号”的字符串。

注意 2:在 python3 中,您需要将 filter() 包装到 list() 以获取列表。

【讨论】:

  • elems = filter(str.isalnum,dict_format_string.split("'")) 应该是 list(elems = filter(str.isalnum,dict_format_string.split("'"))) 而不转换为列出它仍然是“过滤器”对象
【解决方案9】:

总结一下:

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

【讨论】:

  • 除非这不适用于他的单引号字符串,这是他最初问题的一部分。从未提及性能。
  • +1 表示基准(它有助于做出明智的决定),-1 表示结论:正如多次提到的,json 在很多情况下都失败了。应该由用户在功能与性能之间进行选择。
【解决方案10】:

Siva Kameswara Rao Munipalle 的优化代码

s = s.replace("{", "").replace("}", "").split(",")
            
dictionary = {}

for i in s:
    dictionary[i.split(":")[0].strip('\'').replace("\"", "")] = i.split(":")[1].strip('"\'')
            
print(dictionary)

【讨论】:

    【解决方案11】:

    只需使用eval() 函数

    免责声明:如果您使用json.dumps() 或类似函数将字典转换为字符串,则需要注意布尔值。因为此函数将True 转换为true,所以eval() 函数不将truefalse 理解为布尔值,而是变量。所以在这种情况下它会返回一个错误。

    In [1]: import json
    
    In [2]: d = {'a': 1, 'b': '2', 'c': 'True'}
    
    In [3]: d_str = json.dumps(d)
    
    In [4]: d_str
    Out[4]: '{"a": 1, "b": "2", "c": "True"}'
    
    In [5]: eval(d_str)
    Out[5]: {'a': 1, 'b': '2', 'c': 'True'}
    

    【讨论】:

      猜你喜欢
      • 2021-12-01
      • 1970-01-01
      • 2011-05-06
      • 2019-12-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多