Escrever um dicionário para um arquivo txt e lê-lo de volta?

115

Estou tentando escrever um dicionário em um arquivo txt. Em seguida, leia os valores dict digitando as teclas com raw_input. Eu sinto que estou perdendo uma etapa, mas estou procurando há um tempo.

Eu recebo este erro

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

Meu 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)
Cabeça radioativa
fonte
Qual é o problema com este código?
Blender
name 'p' is not defined, p deve ser uma instância da classe Person
avasal de

Respostas:

97

Seu código está quase certo ! Você está certo, você está faltando apenas uma etapa. Ao ler o arquivo, você o está lendo como uma string; mas você quer transformar a string de volta em um dicionário.

A mensagem de erro que você viu foi porque self.whipera uma string, não um dicionário.

Eu escrevi pela primeira vez que você poderia simplesmente alimentar a corda, dict()mas isso não funciona! Você precisa fazer outra coisa.

Exemplo

Aqui está a maneira mais simples: alimentar a string em eval(). Igual a:

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

Você pode fazer isso em uma linha, mas acho que fica confuso desta forma:

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

Mas eval()às vezes não é recomendado. O problema é que eval()avaliará qualquer string, e se alguém o enganou para executar uma string realmente complicada, algo ruim pode acontecer. Nesse caso, você está apenas executando eval()em seu próprio arquivo, então deve estar tudo bem.

Mas, porque eval()é útil, alguém criou uma alternativa mais segura. Isso é chamado literal_evale você o obtém de um módulo Python chamado ast.

import ast

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

ast.literal_eval() avaliará apenas strings que se transformam nos tipos básicos do Python, portanto, não há como uma string complicada fazer algo ruim em seu computador.

EDITAR

Na verdade, a melhor prática em Python é usar uma withinstrução para garantir que o arquivo seja fechado corretamente. Reescrevendo o acima para usar uma withdeclaração:

import ast

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

No Python mais popular, conhecido como "CPython", você geralmente não precisa da withinstrução, pois os recursos integrados de "coleta de lixo" descobrirão que você concluiu o arquivo e o fecharão para você. Mas outras implementações Python, como "Jython" (Python para Java VM) ou "PyPy" (um sistema experimental muito legal com otimização de código just-in-time) podem não descobrir como fechar o arquivo para você. É bom adquirir o hábito de usar withe acho que torna o código muito fácil de entender.

Steveha
fonte
dict()espera um dicionário ou uma lista de tuplas. Não avaliará uma string.
Blender
@Blender Oh, você está certo e eu estou cansado. Vou editar a resposta.
steveha de
@Nick: Tente executá-lo primeiro;)
Blender
@Blender, obrigado pelo aviso. E como estou cansado, acho que vou parar de postar no StackOverflow esta noite! :-)
steveha
175

Você já experimentou o módulo json ? O formato JSON é muito semelhante ao dicionário Python. E é legível / gravável por humanos:

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

Este código é despejado em um arquivo de texto

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

Além disso, você pode carregar de um arquivo JSON:

>>> d2 = json.load(open("text.txt"))
>>> print d2
{u'two': 2, u'one': 1}
KFL
fonte
5
você deve abrir o arquivo para leitura / gravação em uma withdeclaração para garantir que ele feche depois?
ecoe
2
Uau, isso é tão lindo.
zx1986
3
Tive problemas ao usar este método em alguns dicionários complexos, como valores definidos aninhados (por exemplo d = {"one": {"1" : 1, "a": {"i"}}}). Portanto, eu recomendo o método pickle do @blender que funciona no meu caso.
Gürol Canbek
1
Observe que o json do python traduz as chaves de dict em strings ao fazer json.dump, portanto, json.dump({1: 'one'}, file)será lido de volta como {'1': 'one'}ao fazer json.load. Melhor usar pickle se você não for ler o arquivo salvo usando outras linguagens além do Python.
peeol
Existe uma maneira de usar algo como json.dumps (dict_x, indent = 4) para formatar a maneira como o dict é gravado no arquivo para torná-lo mais legível? Quando eu uso seu código, todo o dicionário é escrito em uma linha, não importa o quão grande seja, e não é facilmente legível por humanos.
herteladrian
70

Para armazenar objetos Python em arquivos, use o 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 eu nunca defini b = a, mas em vez disso aselecionei em um arquivo e, em seguida, desfizb .

Quanto ao seu erro:

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

self.whipera um objeto de dicionário. deed.txtcontém texto, então quando você carrega o conteúdo de deed.txtem self.whip, self.whiptorna-se a string representação de de si mesmo.

Você provavelmente gostaria de avaliar a string de volta em um objeto Python:

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

Observe como evalsoa evil. Isso é intencional. Em picklevez disso, use o módulo.

Liquidificador
fonte
não, me dá este erro Arquivo "name.py", linha 24, ao ler print whip [nome] TypeError: os índices da string devem ser inteiros, não str Eu pensei que p = Person () o definiu
Radioactive Head
Esse texto não está em seu código. Antes de fazer uma pergunta, explique o que está errado.
Blender
2
Para armazenar objetos Python em arquivos, use o módulo pickle A menos que sejam arquivos de configuração, então você deseja que sejam editáveis ​​por humanos - consulte Por que se preocupar com arquivos python e de configuração?
Piotr Dobrogost
1
Perfeito. Isso é exatamente o que eu preciso: armazenar o estado da estrutura python nativa em um arquivo que não deve ser editado pelo usuário.
bahamat
Este método funciona perfeitamente para dicionários complexos, como descrevi em meu comentário sobre o método JSON sugerido por @KFL
Gürol Canbek
8

Eu criei minhas próprias funções que funcionam muito bem:

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

Ele armazenará o nome-chave primeiro, seguido por todos os valores. Observe que, neste caso, meu dict contém inteiros, por isso é convertido para int. Esta é provavelmente a parte que você precisa mudar para sua situação.

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
fonte
Ótimo código se seus valores não forem muito longos. Meu dicionário incluía várias cordas longas que acabaram de ser cortadas com '...'
Anne
7

Olá, há uma maneira de escrever e ler o dicionário para o arquivo, você pode transformar seu dicionário no formato JSON e ler e escrever rapidamente, basta fazer isso:

Para escrever sua data:

 import json

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

e para ler seus dados:

 import json

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

Você pode iterar através do par de valores-chave e gravá-lo no arquivo

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
fonte