Écrire un dict dans un fichier txt et le relire?


115

J'essaye d'écrire un dictionnaire dans un fichier txt. Ensuite, lisez les valeurs dict en tapant les touches avec raw_input. J'ai l'impression de manquer une étape mais je cherche depuis un moment maintenant.

J'obtiens cette erreur

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

Mon code:

#!/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)

Quel est le problème avec ce code?
Blender

name 'p' is not defined, p devrait être une instance de la classe Person
disponible le

Réponses:


97

Votre code est presque correct ! Vous avez raison, il vous manque juste une étape. Lorsque vous lisez le fichier, vous le lisez sous forme de chaîne; mais vous souhaitez reconvertir la chaîne en dictionnaire.

Le message d'erreur que vous avez vu était dû au fait qu'il self.whips'agissait d'une chaîne et non d'un dictionnaire.

J'ai d'abord écrit que vous pouviez simplement insérer la chaîne, dict()mais cela ne fonctionne pas! Vous devez faire autre chose.

Exemple

Voici le moyen le plus simple: insérez la chaîne dans eval(). Ainsi:

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

Vous pouvez le faire en une seule ligne, mais je pense que cela semble compliqué de cette façon:

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

Mais ce eval()n'est parfois pas recommandé. Le problème est que eval()cela évaluera n'importe quelle chaîne, et si quelqu'un vous a incité à exécuter une chaîne vraiment délicate, quelque chose de mauvais pourrait se produire. Dans ce cas, vous exécutez simplement eval()votre propre fichier, donc ça devrait aller.

Mais parce que eval()c'est utile, quelqu'un a fait une alternative plus sûre. Ceci est appelé literal_evalet vous l'obtenez à partir d'un module Python appelé ast.

import ast

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

ast.literal_eval() n'évaluera que les chaînes qui se transforment en types Python de base, il n'y a donc aucun moyen qu'une chaîne délicate puisse faire quelque chose de mal sur votre ordinateur.

ÉDITER

En fait, la meilleure pratique en Python consiste à utiliser une withinstruction pour s'assurer que le fichier est correctement fermé. Réécrire ce qui précède pour utiliser une withinstruction:

import ast

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

Dans le Python le plus populaire, connu sous le nom de "CPython", vous n'avez généralement pas besoin de l' withinstruction car les fonctionnalités intégrées de "garbage collection" sauront que vous avez terminé avec le fichier et le fermeront pour vous. Mais d'autres implémentations de Python, comme "Jython" (Python pour la machine virtuelle Java) ou "PyPy" (un système expérimental vraiment sympa avec une optimisation du code juste à temps) pourraient ne pas trouver de fermer le fichier pour vous. Il est bon de prendre l'habitude d'utiliser with, et je pense que cela rend le code assez facile à comprendre.


dict()attend un dictionnaire ou une liste de tuples. Il n'évaluera pas une chaîne.
Blender

@Blender Oh, tu as raison et je suis fatigué. Je vais modifier la réponse.
steveha

@Nick: Essayez d'abord de l'exécuter;)
Blender

@Blender, merci pour le heads-up. Et comme je suis fatigué, je pense que je vais arrêter de poster sur StackOverflow pour la nuit! :-)
steveha

175

Avez-vous essayé le module json ? Le format JSON est très similaire au dictionnaire python. Et c'est lisible / inscriptible par l'homme:

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

Ce code est sauvegardé dans un fichier texte

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

Vous pouvez également charger à partir d'un fichier JSON:

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

5
devez-vous ouvrir le fichier en lecture / écriture dans une withinstruction pour vous assurer qu'il se ferme après?
ecoe

2
WOW, c'est tellement beau.
zx1986

3
J'ai rencontré des problèmes en utilisant cette méthode sur certains dictionnaires complexes tels que des valeurs de jeu imbriquées (par exemple d = {"one": {"1" : 1, "a": {"i"}}}). Par conséquent, je recommande la méthode pickle de @ blender qui fonctionne dans mon cas.
Gürol Canbek le

1
Notez que json de python traduit les clés dict en chaînes lors de l'exécution de json.dump, donc il json.dump({1: 'one'}, file)sera lu comme {'1': 'one'}lors de l'exécution de json.load. Mieux vaut utiliser pickle si vous n'allez pas lire le fichier enregistré en utilisant d'autres langages que Python.
peeol

Existe-t-il un moyen d'utiliser quelque chose comme json.dumps (dict_x, indent = 4) pour formater la façon dont le dict est écrit dans le fichier afin de le rendre plus lisible? Lorsque j'utilise votre code, tout le dict est écrit sur une seule ligne, quelle que soit sa taille, et il n'est pas facilement lisible par l'homme.
herteladrian

70

Pour stocker des objets Python dans des fichiers, utilisez le picklemodule:

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

Notez que je n'ai jamais défini b = a, mais plutôt sélectionné adans un fichier, puis décroché dans b.

Quant à votre erreur:

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

self.whipétait un objet dictionnaire. deed.txtcontient du texte, donc lorsque vous chargez le contenu de deed.txtinto self.whip, self.whipdevient la représentation sous forme de chaîne de lui-même.

Vous voudrez probablement réévaluer la chaîne dans un objet Python:

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

Remarquez comment cela evalressemble evil. C'est intentionnel. Utilisez pickleplutôt le module.


non ça me donne cette erreur Fichier "name.py", ligne 24, en lecture print whip [nom] TypeError: les indices de chaîne doivent être des entiers, pas str Je pensais que p = Person () l'a défini
Radioactive Head

Ce texte n'est pas dans votre code. Avant de poser une question, expliquez ce qui ne va pas.
Blender

2
Pour stocker des objets Python dans des fichiers, utilisez le module pickle À moins qu'il ne s'agisse de fichiers de configuration, vous voulez donc qu'ils soient modifiables par l'homme - voir Pourquoi s'embêter avec python et les fichiers de configuration?
Piotr Dobrogost

1
Parfait. C'est exactement ce dont j'ai besoin: stocker l'état de la structure python native dans un fichier qui n'est pas destiné à être modifiable par l'utilisateur.
bahamat

Cette méthode fonctionne parfaitement pour les dictionnaires complexes comme je l'ai décrit dans mon commentaire pour la méthode JSON suggérée par @KFL
Gürol Canbek

8

J'ai créé mes propres fonctions qui fonctionnent très 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")

Il stockera d'abord le nom de clé, suivi de toutes les valeurs. Notez que dans ce cas, mon dict contient des entiers, c'est pourquoi il se convertit en int. C'est probablement la partie que vous devez changer pour votre situation.

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)

Excellent code si vos valeurs ne sont pas trop longues. Mon dictionnaire comprenait plusieurs longues chaînes qui viennent d'être coupées avec '...'
Anne

7

Salut, il existe un moyen d'écrire et de lire le dictionnaire dans un fichier, vous pouvez transformer votre dictionnaire au format JSON et lire et écrire rapidement, faites ceci:

Pour écrire votre date:

 import json

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

et pour lire vos données:

 import json

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

6

Vous pouvez parcourir la paire clé-valeur et l'écrire dans le fichier

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')
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.