Piszesz dyktando do pliku txt i czytasz go z powrotem?

115

Próbuję napisać słownik do pliku txt. Następnie przeczytaj wartości dict, wpisując klucze za pomocą raw_input. Czuję, że przegapiłem jeden krok, ale szukałem już od jakiegoś czasu.

Otrzymuję ten błąd

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

Mój kod:

#!/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)
Radioaktywna głowa
źródło
Jaki jest problem z tym kodem?
Blender,
name 'p' is not defined, p powinno być instancją klasy Person
avasal

Odpowiedzi:

97

Twój kod jest prawie poprawny ! Masz rację, brakuje Ci tylko jednego kroku. Kiedy czytasz plik, czytasz go jako ciąg; ale chcesz przekształcić ciąg z powrotem w słownik.

Wyświetlony komunikat o błędzie brzmiał, ponieważ self.whipbył to ciąg znaków, a nie słownik.

Najpierw napisałem, że możesz po prostu wprowadzić ciąg, dict()ale to nie działa! Musisz zrobić coś innego.

Przykład

Oto najprostszy sposób: wprowadź ciąg do eval(). Tak jak to:

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

Możesz to zrobić w jednej linii, ale myślę, że wygląda to niechlujnie w ten sposób:

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

Ale eval()czasami nie jest zalecane. Problem polega na tym, eval()że oceni każdy ciąg, a jeśli ktoś oszuka cię do uruchomienia naprawdę trudnego ciągu, może się zdarzyć coś złego. W takim przypadku po prostu uruchamiasz eval()swój własny plik, więc powinno być w porządku.

Ale ponieważ eval()jest przydatny, ktoś stworzył dla niego alternatywę, która jest bezpieczniejsza. Nazywa się to literal_evali uzyskujesz to z modułu Pythona o nazwie ast.

import ast

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

ast.literal_eval() oceni tylko łańcuchy, które zamieniają się w podstawowe typy Pythona, więc nie ma możliwości, aby skomplikowany ciąg mógł zrobić coś złego na twoim komputerze.

EDYTOWAĆ

Właściwie najlepszą praktyką w Pythonie jest użycie withinstrukcji, aby upewnić się, że plik zostanie poprawnie zamknięty. Przepisanie powyższego na withoświadczenie:

import ast

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

W najpopularniejszym Pythonie, znanym jako „CPython”, zwykle nie potrzebujesz withinstrukcji, ponieważ wbudowane funkcje „usuwania elementów bezużytecznych” zorientują się, że skończyłeś z plikiem i zamkną go za Ciebie. Ale inne implementacje Pythona, takie jak „Jython” (Python dla maszyny wirtualnej Java) lub „PyPy” (naprawdę fajny system eksperymentalny z optymalizacją kodu w trybie just-in-time) mogą nie zamknąć pliku za Ciebie. Dobrze jest przyzwyczaić się do używania withi myślę, że dzięki temu kod jest łatwy do zrozumienia.

steveha
źródło
dict()oczekuje słownika lub listy krotek. Nie oceni ciągu.
Blender,
@Blender Och, masz rację i jestem zmęczony. Zmienię odpowiedź.
steveha
@Nick: Spróbuj najpierw uruchomić;)
Blender,
@Blender, dziękuję za ostrzeżenie. A ponieważ jestem zmęczony, myślę, że przestanę pisać na StackOverflow na noc! :-)
steveha
175

Czy próbowałeś już modułu JSON ? Format JSON jest bardzo podobny do słownika Pythona. I jest czytelny / zapisywalny dla człowieka:

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

Ten kod zrzuca do pliku tekstowego

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

Możesz także załadować z pliku JSON:

>>> d2 = json.load(open("text.txt"))
>>> print d2
{u'two': 2, u'one': 1}
KFL
źródło
5
czy powinieneś otworzyć plik do odczytu / zapisu w withoświadczeniu, aby upewnić się, że zostanie zamknięty po?
ecoe
2
WOW, to jest takie piękne.
zx1986
3
Doświadczyłem problemów z użyciem tej metody w przypadku niektórych złożonych słowników, takich jak posiadanie zagnieżdżonych wartości zadanych (np d = {"one": {"1" : 1, "a": {"i"}}}.). Dlatego polecam metodę marynowania @ blendera, która działa w moim przypadku.
Gürol Canbek
1
Zwróć uwagę, że json języka Python tłumaczy klucze dyktowania na ciągi znaków podczas wykonywania json.dump, więc json.dump({1: 'one'}, file)czyta z powrotem tak, jak {'1': 'one'}podczas wykonywania json.load. Lepiej użyć pikle, jeśli nie zamierzasz czytać zapisanego pliku w innych językach niż Python.
peeol
Czy istnieje sposób na użycie czegoś takiego jak json.dumps (dict_x, indent = 4), aby sformatować sposób, w jaki dict jest zapisywany w pliku, aby był bardziej czytelny? Kiedy używam twojego kodu, cały dykt jest zapisywany w jednej linii, bez względu na to, jak duży jest i nie jest łatwy do odczytania przez człowieka.
herteladrian
70

Aby przechowywać obiekty Pythona w plikach, użyj picklemodułu:

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

Zauważ, że nigdy nie ustawiłem b = a, ale zamiast tego wytrawiłem ado pliku, a następnie rozpakowałem go b.

Co do twojego błędu:

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

self.whipbył obiektem słownikowym. deed.txtzawiera tekst, więc po załadowaniu zawartości deed.txtdo self.whip, self.whipstaje się ciągową reprezentacją samego siebie.

Prawdopodobnie chciałbyś przeliczyć ciąg z powrotem na obiekt Pythona:

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

Zwróć uwagę, jak evalbrzmi evil. To jest zamierzone. pickleZamiast tego użyj modułu.

Mikser
źródło
nie, to daje mi ten błąd Plik "name.py", wiersz 24, w czytaniu print whip [nazwa] TypeError: indeksy stringów muszą być liczbami całkowitymi, a nie str. Myślałem, że p = Person () zdefiniował to
Radioactive Head
Tego tekstu nie ma w Twoim kodzie. Zanim zadasz pytanie, wyjaśnij, co się stało.
Blender,
2
Aby przechowywać obiekty Pythona w plikach, użyj modułu pickle. Jeśli nie są to pliki konfiguracyjne, więc chcesz, aby były edytowalne przez człowieka - zobacz Po co zawracać sobie głowę pythonem i plikami konfiguracyjnymi?
Piotr Dobrogost
1
Idealny. Właśnie tego potrzebuję: do przechowywania stanu struktury natywnego języka Python w pliku, który nie jest przeznaczony do edycji przez użytkownika.
Bahamat
Ta metoda działa doskonale w przypadku złożonych słowników, jak opisałem w moim komentarzu do metody JSON zasugerowanej przez @KFL
Gürol Canbek
8

Stworzyłem własne funkcje, które działają naprawdę fajnie:

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

Najpierw zapisze nazwę klucza, a po niej wszystkie wartości. Zauważ, że w tym przypadku moja dict zawiera liczby całkowite, dlatego jest konwertowana na int. Najprawdopodobniej jest to część, którą musisz zmienić w swojej sytuacji.

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
źródło
Świetny kod, jeśli twoje wartości nie są zbyt długie. Mój słownik zawierał wiele długich ciągów, które właśnie zostały odcięte przez „...”
Anne
7

Cześć, jest sposób na zapisanie i odczytanie słownika do pliku, w którym możesz zamienić słownik na format JSON oraz szybko czytać i pisać, po prostu zrób to:

Aby napisać datę:

 import json

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

i odczytać Twoje dane:

 import json

 f = open('destFile.txt', 'r')
 your_dictionary = json.loads(f.read())
Manouchehr Rasouli
źródło
6

Możesz iterować przez parę klucz-wartość i zapisać ją do pliku

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
źródło