¿Escribir un dictado en un archivo txt y volver a leerlo?

115

Estoy tratando de escribir un diccionario en un archivo txt. Luego lea los valores de dict escribiendo las teclas con raw_input. Siento que me falta un paso, pero he estado buscando por un tiempo.

Me sale este error

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

Mi código:

#!/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)
Cabeza radiactiva
fuente
¿Cuál es el problema con este código?
Blender
name 'p' is not defined, p debería ser una instancia de la clase Person
avasal

Respuestas:

97

¡Tu código es casi correcto ! Tienes razón, solo te falta un paso. Cuando lee el archivo, lo está leyendo como una cadena; pero desea convertir la cadena de nuevo en un diccionario.

El mensaje de error que vio fue porque self.whipera una cadena, no un diccionario.

Primero escribí que podrías simplemente introducir la cadena, ¡ dict()pero eso no funciona! Necesitas hacer otra cosa.

Ejemplo

Esta es la forma más sencilla: introduzca la cuerda eval(). Al igual que:

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

Puedes hacerlo en una línea, pero creo que se ve desordenado de esta manera:

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

Pero a eval()veces no se recomienda. El problema es que eval()evaluará cualquier cadena, y si alguien lo engaña para que ejecute una cadena realmente complicada, podría suceder algo malo. En este caso, solo está ejecutando eval()su propio archivo, por lo que debería estar bien.

Pero debido a que eval()es útil, alguien creó una alternativa que es más segura. Esto se llama literal_evaly lo obtienes de un módulo de Python llamado ast.

import ast

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

ast.literal_eval() solo evaluará cadenas que se conviertan en los tipos básicos de Python, por lo que no hay forma de que una cadena complicada pueda hacer algo malo en su computadora.

EDITAR

En realidad, la mejor práctica en Python es usar una withdeclaración para asegurarse de que el archivo se cierre correctamente. Reescribiendo lo anterior para usar una withdeclaración:

import ast

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

En el Python más popular, conocido como "CPython", por lo general no necesita la withdeclaración ya que las funciones integradas de "recolección de basura" se darán cuenta de que ha terminado con el archivo y lo cerrarán por usted. Pero otras implementaciones de Python, como "Jython" (Python para la máquina virtual de Java) o "PyPy" (un sistema experimental realmente genial con optimización de código justo a tiempo) podrían no cerrar el archivo por usted. Es bueno tener el hábito de usarlo with, y creo que hace que el código sea bastante fácil de entender.

steveha
fuente
dict()espera un diccionario o una lista de tuplas. No evaluará una cadena.
Blender
@Blender Oh, tienes razón y estoy cansado. Editaré la respuesta.
steveha
@Nick: Intenta ejecutarlo primero;)
Blender
@Blender, gracias por el aviso. ¡Y como estoy cansado, creo que dejaré de publicar en StackOverflow por la noche! :-)
steveha
175

¿Has probado el módulo json ? El formato JSON es muy similar al diccionario de Python. Y es legible / escribible por humanos:

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

Este código se vuelca en un archivo de texto

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

También puede cargar desde un archivo JSON:

>>> d2 = json.load(open("text.txt"))
>>> print d2
{u'two': 2, u'one': 1}
KFL
fuente
5
¿Debería abrir el archivo para leerlo / escribirlo en una withdeclaración para asegurarse de que se cierre después?
ecoe
2
Oh, esto es tan bello.
zx1986
3
Experimenté problemas al usar este método en algunos diccionarios complejos, como tener valores establecidos anidados (por ejemplo d = {"one": {"1" : 1, "a": {"i"}}}). Por lo tanto, recomiendo el método pickle de @ blender que funciona en mi caso.
Gürol Canbek
1
Tenga en cuenta que el json de python traduce las claves de dictado a cadenas cuando se hace json.dump, por lo que json.dump({1: 'one'}, file)se leerá como {'1': 'one'}cuando se hace json.load. Es mejor usar pickle si no va a leer el archivo guardado en otros lenguajes que no sean Python.
peeol
¿Hay alguna manera de usar algo como json.dumps (dict_x, indent = 4) para formatear la forma en que se escribe el dict en el archivo para hacerlo más legible? Cuando uso su código, todo el diccionario está escrito en una línea, no importa cuán grande sea, y no es fácilmente legible por humanos.
herteladrian
70

Para almacenar objetos de Python en archivos, use el picklemódulo:

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

Observe que nunca configuré b = a, sino aque encurtí en un archivo y luego lo desenvolví en b.

En cuanto a tu error:

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

self.whipera un objeto de diccionario. deed.txtcontiene texto, por lo que cuando carga el contenido de deed.txten self.whip, se self.whipconvierte en la representación de cadena de sí mismo.

Probablemente desee evaluar la cadena de nuevo en un objeto Python:

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

Observe cómo evalsuena evil. Eso es intencional. En su lugar, utilice el picklemódulo.

Licuadora
fuente
no, me da este error Archivo "name.py", línea 24, al leer print whip [nombre] TypeError: los índices de cadena deben ser enteros, no str. Pensé que p = Person () lo definió
Radioactive Head
Ese texto no está en tu código. Antes de hacer una pregunta, explique qué está mal.
Blender
2
Para almacenar objetos de Python en archivos, use el módulo pickle A menos que estos sean archivos de configuración, por lo que desea que sean editables por humanos; consulte ¿Por qué molestarse con los archivos de configuración y Python?
Piotr Dobrogost
1
Perfecto. Esto es exactamente lo que necesito: almacenar el estado de la estructura nativa de Python en un archivo que no está destinado a ser editable por el usuario.
bahamat
Este método funciona perfectamente para diccionarios complejos como describí en mi comentario para el método JSON sugerido por @KFL
Gürol Canbek
8

Creé mis propias funciones que funcionan muy bien:

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

Primero almacenará el nombre clave, seguido de todos los valores. Tenga en cuenta que en este caso mi dictado contiene números enteros, por eso se convierte en int. Esta es probablemente la parte que necesita cambiar para su situación.

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)
PascalVKooten
fuente
Excelente código si sus valores no son demasiado largos. Mi diccionario incluía varias cadenas largas que acababan de cortarse con '...'
Anne
7

Hola, hay una manera de escribir y leer el diccionario en un archivo, puede convertir su diccionario en formato JSON y leer y escribir rápidamente, solo haga esto:

Para escribir tu fecha:

 import json

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

y leer sus datos:

 import json

 f = open('destFile.txt', 'r')
 your_dictionary = json.loads(f.read())
Manouchehr Rasouli
fuente
6

Puede iterar a través del par clave-valor y escribirlo en el archivo

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')
Aravind Krishnakumar
fuente