แปลงการแทนค่าสตริงของพจนานุกรมเป็นพจนานุกรมหรือไม่?


767

ฉันจะแปลงการstrแทนค่าของ a dictเช่นสตริงต่อไปนี้เป็น a ได้dictอย่างไร?

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

evalฉันไม่ต้องการใช้งาน ฉันจะใช้อะไรได้อีก

เหตุผลหลักสำหรับเรื่องนี้คือหนึ่งในเพื่อนร่วมชั้นเรียนของฉันที่เขาเขียนแปลงข้อมูลทั้งหมดเป็นสตริง ฉันไม่ได้อยู่ในอารมณ์ที่จะไปและปรับเปลี่ยนชั้นเรียนของเขาเพื่อจัดการกับปัญหานี้


1
หากคุณไม่สามารถใช้ Python 2.6 ได้คุณสามารถใช้การฝังรากฟันเทียมแบบปลอดภัยอย่างง่ายๆเช่นcode.activestate.com/recipes/364469 Piggybacks บนคอมไพเลอร์ Python เพื่อให้คุณไม่ต้องทำงานทั้งหมดด้วยตัวเอง
Ned Batchelder

11
หมายเหตุ : สำหรับผู้ที่มาที่นี่พร้อมกับข้อมูลJSON ที่ดูคล้าย ๆ กัน คุณต้องการอ่านParse JSON ใน Pythonแทน JSON คือไม่ได้เป็นสิ่งเดียวกับงูหลาม หากคุณมีเครื่องหมายคำพูดล้อมรอบสตริงของคุณคุณอาจมีข้อมูล JSON นอกจากนี้คุณยังสามารถมองหา, หรือ, Python ใช้ไวยากรณ์, และ "nulltruefalseNoneTrueFalse
Martijn Pieters

คำตอบ:


1167

การเริ่มต้นใน Python 2.6 คุณสามารถใช้บิวด์อินast.literal_eval:

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

evalนี้จะปลอดภัยกว่าการใช้ ตามเอกสารของตัวเองบอกว่า:

>>> ความช่วยเหลือ (ast.literal_eval)
ช่วยเหลือฟังก์ชั่น literal_eval ในโมดูล ast:

literal_eval (node_or_string)
    ประเมินโหนดการแสดงออกหรือสตริงที่มี Python อย่างปลอดภัย
    การแสดงออก สตริงหรือโหนดที่ระบุอาจประกอบด้วยสิ่งต่อไปนี้เท่านั้น
    โครงสร้างตัวอักษร Python: สตริง, ตัวเลข, tuples, รายการ, dicts, booleans,
    และไม่มี

ตัวอย่างเช่น:

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

ฉันได้รับข้อผิดพลาดนี้ฉันอยู่บนหลาม 2.6 (x86) บน windows 7 x64 ไฟล์ "D: \ Python26 \ lib \ ast.py", บรรทัดที่ 48, ใน literal_eval node_or_string = แยกวิเคราะห์ (node_or_string, mode = 'eval') ไฟล์ "D : \ Python26 \ lib \ ast.py ", บรรทัดที่ 36, ในการแยกวิเคราะห์คอมไพล์ (expr, ชื่อไฟล์, โหมด, PyCF_ONLY_AST) ไฟล์" <unknown> ", บรรทัด 1 ^ SyntaxError: ไวยากรณ์ที่ไม่ถูกต้อง

สิ่งที่เกี่ยวกับ"dict(a=1)"สตริงสไตล์?
n611x007

สิ่งนี้ดูเหมือนจะไม่ทำงานสำหรับค่า enum ภายในพจนานุกรม เช่น: d = "{'col': <Colors.RED: 2>, 'val': 2}"
shivshnkr

3
ทำไมไม่ใช้ json.dumps และ json.loads insead ฉันพบวิธีแก้ปัญหานี้สูงกว่าการใช้ 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'}

โปรดทราบว่าหากคุณมีเครื่องหมายคำพูดเดี่ยวเป็นส่วนหนึ่งของคีย์หรือค่าของคุณสิ่งนี้จะล้มเหลวเนื่องจากการแทนที่อักขระที่ไม่เหมาะสม แนะนำให้ใช้วิธีนี้เฉพาะในกรณีที่คุณไม่ชอบวิธีแก้ปัญหา eval

เพิ่มเติมเกี่ยวกับการอ้างคำสั่ง json เดี่ยว: jQuery.parseJSON โยนข้อผิดพลาด“ JSON ไม่ถูกต้อง” เนื่องจากหนีคำพูดเดียวใน JSON


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

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

1
ฉันกำลังมองหาวิธีแก้ปัญหานี้ +1สำหรับการแจ้งว่าตัวถอดรหัสต้องการอัญประกาศคู่รอบคีย์และค่า
h8pathak

"{0: 'Hello'}"ปัญหาอีกประการหนึ่งคือ
ฟินน์Årup Nielsen

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) เอาต์พุตการพิมพ์ d คือ: {u'1 ': u'P ', u'3': u'M ', u'2': u'N '}
user905

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

อย่างไรก็ตามมีข้อ จำกัด เช่น: h = '{"muffin": "lolz", "foo": "kitty",}', h = '{"muffin": "lolz", "foo": "kitty "} ', (เพิ่งสังเกตเห็นเป็นส่วนหนึ่งของความคิดเห็นเดียวกันในคำตอบที่คล้ายกัน ... ยังคงออกจากที่นี่เพื่อความสมบูรณ์ ... )
ntg

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
นี่จะทำให้สตริง 'ใช่' และ 'ไม่' ถูกแปลงเป็นจริง / เท็จ
Eric Marcos

23

ถ้าสตริงนั้นเชื่อถือได้ตลอดเวลาคุณสามารถใช้eval(หรือใช้literal_evalตามที่แนะนำได้มันปลอดภัยไม่ว่าสตริงนั้นจะเป็นอะไร) มิฉะนั้นคุณต้องมีโปรแกรมแยกวิเคราะห์ ตัวแยกวิเคราะห์ JSON (เช่น simplejson) จะทำงานถ้าเขาเก็บเนื้อหาที่เหมาะกับแบบแผน JSON เท่านั้น


8
การเริ่มต้นใน 2.6 simplejson จะรวมอยู่ในไลบรารีมาตรฐาน Python เป็นโมดูล json
Eli Courtwright

11
ใช่นั่นเป็นคำตอบที่ดี แต่โปรดทราบว่า JSON อย่างเป็นทางการไม่สนับสนุนสตริงที่มีการเสนอราคาเดียวตามที่ระบุไว้ในตัวอย่างของโปสเตอร์ต้นฉบับ
Ben Hoyt

19

jsonใช้ astห้องสมุดสิ้นเปลืองหน่วยความจำและลงและช้าลง ฉันมีกระบวนการที่จำเป็นต้องอ่านไฟล์ข้อความขนาด 156Mb Astหน่วงเวลา 5 นาทีสำหรับพจนานุกรมการแปลงjsonและ 1 นาทีโดยใช้หน่วยความจำน้อยลง 60%!


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
ว้าว .... สุดยอดคำอธิบาย ....
smack cherry

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เกิดอะไรขึ้นถ้าค่ามี,?
Om Sao

4

ไม่มีการใช้ libs ใด ๆ :

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))

หมายเหตุ: เนื่องจาก hardcoded split("'")จะทำงานเฉพาะกับสตริงที่ข้อมูลเป็น "การอ้างอิงเดียว"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.