การเขียน dict ไปยังไฟล์ txt และอ่านกลับ?


115

ฉันพยายามเขียนพจนานุกรมลงในไฟล์ txt อ่านแล้วค่า Dict raw_inputโดยการพิมพ์คีย์ด้วย ฉันรู้สึกว่าฉันพลาดไปเพียงก้าวเดียว แต่ฉันมองหามาระยะหนึ่งแล้ว

ฉันได้รับข้อผิดพลาดนี้

File "name.py", line 24, in reading
    print whip[name]
TypeError: string indices must be integers, not str

รหัสของฉัน:

#!/usr/bin/env python
from sys import exit

class Person(object):
    def __init__(self):
        self.name = ""
        self.address = ""
        self.phone = ""
        self.age = ""
        self.whip = {}

    def writing(self):
        self.whip[p.name] = p.age, p.address, p.phone
        target = open('deed.txt', 'a')
        target.write(str(self.whip))
        print self.whip

    def reading(self):
        self.whip = open('deed.txt', 'r').read()
        name = raw_input("> ")
        if name in self.whip:
            print self.whip[name]

p = Person()

while True:
    print "Type:\n\t*read to read data base\n\t*write to write to data base\n\t*exit to exit"
    action = raw_input("\n> ")
    if "write" in action:
        p.name = raw_input("Name?\n> ")
        p.phone = raw_input("Phone Number?\n> ")
        p.age = raw_input("Age?\n> ")
        p.address = raw_input("Address?\n>")
        p.writing()
    elif "read" in action:
        p.reading()
    elif "exit" in action:
        exit(0)

รหัสนี้มีปัญหาอะไร
Blender

name 'p' is not defined, p ควรเป็นตัวอย่างของคลาส Person
avasal

คำตอบ:


97

รหัสของคุณเกือบถูกต้อง ! คุณพูดถูกคุณพลาดเพียงก้าวเดียว เมื่อคุณอ่านในไฟล์คุณกำลังอ่านเป็นสตริง แต่คุณต้องการเปลี่ยนสตริงกลับเป็นพจนานุกรม

ข้อความแสดงข้อผิดพลาดที่คุณเห็นเป็นเพราะself.whipเป็นสตริงไม่ใช่พจนานุกรม

ก่อนอื่นฉันเขียนว่าคุณสามารถป้อนสตริงเข้าไปได้dict()แต่ไม่ได้ผล! คุณต้องทำอย่างอื่น

ตัวอย่าง

นี่คือวิธีที่ง่ายที่สุด: eval()ฟีดสตริงเข้า ชอบมาก:

def reading(self):
    s = open('deed.txt', 'r').read()
    self.whip = eval(s)

คุณสามารถทำได้ในบรรทัดเดียว แต่ฉันคิดว่ามันดูยุ่งเหยิงด้วยวิธีนี้:

def reading(self):
    self.whip = eval(open('deed.txt', 'r').read())

แต่eval()บางครั้งก็ไม่แนะนำ ปัญหาคือeval()จะประเมินสตริงใด ๆและหากมีคนหลอกให้คุณเรียกใช้สตริงที่ยุ่งยากจริงๆสิ่งที่ไม่ดีอาจเกิดขึ้น ในกรณีนี้คุณกำลังเรียกใช้eval()ไฟล์ของคุณเองดังนั้นจึงน่าจะโอเค

แต่เนื่องจากeval()มีประโยชน์จึงมีคนทำทางเลือกที่ปลอดภัยกว่า นี้เรียกว่าและคุณได้รับมันจากโมดูลงูใหญ่เรียกว่าliteral_evalast

import ast

def reading(self):
    s = open('deed.txt', 'r').read()
    self.whip = ast.literal_eval(s)

ast.literal_eval() จะประเมินเฉพาะสตริงที่กลายเป็นประเภท Python พื้นฐานดังนั้นจึงไม่มีวิธีใดที่สตริงที่ยุ่งยากสามารถทำสิ่งที่ไม่ดีบนคอมพิวเตอร์ของคุณได้

แก้ไข

จริงๆแล้วแนวทางปฏิบัติที่ดีที่สุดใน Python คือการใช้withคำสั่งเพื่อให้แน่ใจว่าไฟล์ถูกปิดอย่างถูกต้อง การเขียนใหม่ข้างต้นเพื่อใช้withคำสั่ง:

import ast

def reading(self):
    with open('deed.txt', 'r') as f:
        s = f.read()
        self.whip = ast.literal_eval(s)

ใน Python ที่ได้รับความนิยมมากที่สุดหรือที่เรียกว่า "CPython" โดยปกติคุณไม่จำเป็นต้องใช้withคำสั่งนี้เนื่องจากคุณลักษณะ "การรวบรวมขยะ" ในตัวจะคิดว่าคุณได้ทำไฟล์เสร็จแล้วและจะปิดให้คุณ แต่การใช้งาน Python อื่น ๆ เช่น "Jython" (Python สำหรับ Java VM) หรือ "PyPy" (ระบบทดลองที่ยอดเยี่ยมจริงๆพร้อมการเพิ่มประสิทธิภาพโค้ดแบบทันเวลา) อาจไม่คิดที่จะปิดไฟล์ให้คุณ เป็นเรื่องดีที่จะใช้เป็นนิสัยwithและฉันคิดว่ามันทำให้โค้ดเข้าใจง่าย


dict()คาดว่าจะมีพจนานุกรมหรือรายการสิ่งที่เพิ่มขึ้น จะไม่ประเมินสตริง
Blender

@Blender โอ้ยยยยยยยยยยยยยยยย ฉันจะแก้ไขคำตอบ
steveha

@Nick: ลองรันดูก่อน;)
Blender

@ เบลนเดอร์ขอบคุณสำหรับการแจ้งเตือน และเนื่องจากฉันเหนื่อยฉันคิดว่าฉันจะหยุดโพสต์บน StackOverflow ในคืนนี้! :-)
steveha

175

คุณลองโมดูล json แล้วหรือยัง? รูปแบบ JSON คล้ายกับพจนานุกรม python มาก และมนุษย์สามารถอ่าน / เขียนได้:

>>> import json
>>> d = {"one":1, "two":2}
>>> json.dump(d, open("text.txt",'w'))

รหัสนี้ทิ้งไปยังไฟล์ข้อความ

$ cat text.txt 
{"two": 2, "one": 1}

นอกจากนี้คุณสามารถโหลดจากไฟล์ JSON:

>>> d2 = json.load(open("text.txt"))
>>> print d2
{u'two': 2, u'one': 1}

5
คุณควรเปิดไฟล์เพื่ออ่าน / เขียนในwithคำสั่งเพื่อให้แน่ใจว่าปิดแล้วหรือไม่?
ecoe

2
ว้าวสวยมาก
zx1986

3
ฉันประสบปัญหาในการใช้วิธีนี้กับพจนานุกรมที่ซับซ้อนบางตัวซึ่งมีค่าชุดซ้อนกัน (เช่นd = {"one": {"1" : 1, "a": {"i"}}}) ดังนั้นฉันขอแนะนำวิธีการดองของ @ blender ที่ใช้ได้ในกรณีของฉัน
Gürol Canbek

1
โปรดทราบว่า json ของ python แปลคีย์ dict เป็นสตริงเมื่อทำ json.dump ดังนั้นjson.dump({1: 'one'}, file)จะอ่านกลับเหมือน{'1': 'one'}เมื่อทำ json.load ควรใช้ pickle ถ้าคุณจะไม่อ่านไฟล์ที่บันทึกไว้โดยใช้ภาษาอื่นที่ไม่ใช่ Python
peeol

มีวิธีใช้ json.dumps (dict_x, indent = 4) เพื่อจัดรูปแบบวิธีการเขียน dict ลงในไฟล์เพื่อให้อ่านง่ายขึ้นหรือไม่? เมื่อฉันใช้รหัสของคุณคำสั่งทั้งหมดจะถูกเขียนเป็นบรรทัดเดียวไม่ว่ามันจะใหญ่แค่ไหนก็ตามและไม่ใช่สิ่งที่มนุษย์จะอ่านได้ง่ายๆ
herteladrian

70

ในการจัดเก็บวัตถุ Python ในไฟล์ให้ใช้pickleโมดูล:

import pickle

a = {
  'a': 1,
  'b': 2
}

with open('file.txt', 'wb') as handle:
  pickle.dump(a, handle)

with open('file.txt', 'rb') as handle:
  b = pickle.loads(handle.read())

print a == b # True

สังเกตว่าฉันไม่เคยตั้ง b = aแต่aจะเก็บไฟล์ไว้ที่ไฟล์แล้วยกเลิกการbเลือก

สำหรับข้อผิดพลาดของคุณ:

self.whip = open('deed.txt', 'r').read()

self.whipเป็นวัตถุพจนานุกรม deed.txtมีข้อความดังนั้นเมื่อคุณโหลดเนื้อหาของdeed.txtเข้าself.whip, self.whipกลายเป็นสตริงเป็นตัวแทนของตัวเอง

คุณอาจต้องการประเมินสตริงกลับไปที่วัตถุ Python:

self.whip = eval(open('deed.txt', 'r').read())

แจ้งให้ทราบว่าเสียงเหมือนeval evilนั่นเป็นความตั้งใจ ใช้pickleโมดูลแทน


ไม่ให้ฉันมีข้อผิดพลาดนี้ File "name.py" บรรทัดที่ 24 ในการอ่านแส้พิมพ์ [name] TypeError: ดัชนีสตริงต้องเป็นจำนวนเต็มไม่ใช่ str ฉันคิดว่า p = Person () กำหนดไว้
Radioactive Head

ข้อความนั้นไม่อยู่ในรหัสของคุณ ก่อนถามคำถามอธิบายว่ามีอะไรผิดปกติ
Blender

2
ในการจัดเก็บออบเจ็กต์ Python ในไฟล์ให้ใช้ pickle moduleเว้นแต่ว่าไฟล์เหล่านี้จะเป็นไฟล์ config ดังนั้นคุณจึงต้องการให้มนุษย์สามารถแก้ไขได้ - ดูทำไมต้องกังวลกับ python และไฟล์ config?
Piotr Dobrogost

1
สมบูรณ์ นี่คือสิ่งที่ฉันต้องการ: เพื่อจัดเก็บสถานะโครงสร้าง python ดั้งเดิมในไฟล์ที่ไม่ได้ตั้งใจให้ผู้ใช้แก้ไขได้
บาฮามาส

วิธีนี้ใช้ได้ดีกับพจนานุกรมที่ซับซ้อนตามที่ฉันอธิบายไว้ในความคิดเห็นของฉันสำหรับวิธีการ JSON ที่แนะนำโดย @KFL
Gürol Canbek

8

ฉันสร้างฟังก์ชั่นของตัวเองซึ่งใช้งานได้ดีจริงๆ:

def writeDict(dict, filename, sep):
    with open(filename, "a") as f:
        for i in dict.keys():            
            f.write(i + " " + sep.join([str(x) for x in dict[i]]) + "\n")

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

def readDict(filename, sep):
    with open(filename, "r") as f:
        dict = {}
        for line in f:
            values = line.split(sep)
            dict[values[0]] = {int(x) for x in values[1:len(values)]}
        return(dict)

รหัสที่ดีหากค่าของคุณไม่ยาวเกินไป พจนานุกรมของฉันมีสตริงยาวหลายสายซึ่งเพิ่งถูกตัดออกด้วย '... '
แอน

7

สวัสดีมีวิธีเขียนและอ่านพจนานุกรมเป็นไฟล์คุณสามารถเปลี่ยนพจนานุกรมของคุณเป็นรูปแบบ JSON และอ่านและเขียนได้อย่างรวดเร็วเพียงทำสิ่งนี้:

ในการเขียนวันที่ของคุณ:

 import json

 your_dictionary = {"some_date" : "date"}
 f = open('destFile.txt', 'w+')
 f.write(json.dumps(your_dictionary))

และอ่านข้อมูลของคุณ:

 import json

 f = open('destFile.txt', 'r')
 your_dictionary = json.loads(f.read())

6

คุณสามารถวนซ้ำผ่านคู่คีย์ - ค่าและเขียนลงในไฟล์

pair = {'name': name,'location': location}
with open('F:\\twitter.json', 'a') as f:
     f.writelines('{}:{}'.format(k,v) for k, v in pair.items())
     f.write('\n')
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.