Python Vokabeltrainer Probleme bei txt Dateien auslesen und speichern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
McAffe
User
Beiträge: 4
Registriert: Mittwoch 30. September 2020, 17:20

Ich versuche einen Vokabeltrainer zu programmieren, aber ich schaffe es nicht, dass das Programm die Vokabellisten abspeichert und bei einem Neustart ausliest. Das Problem ist, dass mein Programm die Vokabeln nicht erkennt oder fehlerhaft abspeichert. Wenn ich den Code beim Abspeichern ohne \n schreibe funktioniert es besser nur erkennt er dann die einzelnen Vokabeln durch den readline befehl nicht mehr und sieht alles als eine Vokabel.

Vielen Dank für Eure Hilfe.

Code: Alles auswählen

import random

class Entry:
    def __init__(self, deutsch, englisch):
        self.deutsch = deutsch
        self.englisch = englisch
    def toStringd(self):
        return self.deutsch
    def toStringe(self):
        return self.englisch

d = open('deutsch.txt', 'r')
e = open('englisch.txt', 'r')

eintraege = [Entry(d.readline(), e.readline())]

speichern = []

d.close()
e.close()

def eingabe():
    while True:
        deutsch = input("Deutsches Wort: ")
        if deutsch == "#":
            break
        englisch = input("Englisches Wort: ")
        if englisch == "#":
            break
        eintraege.append(Entry(deutsch, englisch))
        speichern.append(Entry(deutsch, englisch))

def abfrage():
    while True:
        i = random.randint(0, len(eintraege)-1)
        englisch = input("Englische Übersetzung von " + eintraege[i].deutsch + ": ")
        if(englisch == "#"):
            return
        if eintraege[i].englisch == englisch:
            print("korrekt")
        else:
            print("falsch, richtige Antwort ist: " + eintraege[i].englisch)

def printall():
    for eintrag in eintraege:
        print(eintrag.toStringd() + eintrag.toStringe())

while True:
    befehl = input("Befehl:")
    if befehl == "eingabe":
        eingabe()
    elif befehl == "abfrage":
        abfrage()
    elif befehl == "beenden":
        break
    elif befehl == "ausgabe":
        printall()
    elif befehl == "hilfe":
        print("Mögliche Befehle:", "\n eingabe", "\n abfrage", "\n beenden", "\n ausgabe", "\n hilfe",sep="")
    else:
        print("Keine bekannte Ausgabe. Für alle Befehle tippe: hilfe.")

d2 = open('deutsch.txt', 'a')

for eintrag in speichern:
    d2.write(eintrag.toStringd()+'\n')

d2.close()

e2 = open('englisch.txt', 'a')

for eintrag in speichern:
    e2.write(eintrag.toStringe()+'\n')

e2.close()

print("Beenden")

Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Hallo,

du solltest lernen Funktionen richtig zu benutzen. Eine funktion bekommt Ihre Werte per Paramter übergeben und gibt dann das ergebnis per return zurück.
Es sollte kein Code auf Modulebene stehen, außer Konstanten und der if __name__ == '__main__': Aufruf.
Die Klasse ist hier unnötig.
Dateien öffnet man mit dem with-Statement, da spart man sich das schließen am Ende. Auch sollte das encoding immer angegeben werden.
Strings stückelt man nicht mit + zusammen, dafür gibt es Stringformatierung bzw f-Strings.

Ich würde das ganze so umsetzen: (ich hääte zwwar eigentlich nur eine Textdatei verwendet, habe es aber jetzt hier mal mit zwei unterschiedlichen gemacht. Dabei ist die Reihenfolge der Wörter in der Textdatei wichtig.

Code: Alles auswählen

import random

GERMAN_TEXTFILE ="german.txt"
ENGLISH_TEXTFILE = "englisch.txt"

def read_file_as_list(filename):
    with open(filename, "r", encoding='UTF-8') as input_file:
        return [word.strip("\n") for word in input_file.readlines()] 

def add_words_to_textfiles(german_word, english_word):
    with open(GERMAN_TEXTFILE, "a", encoding='UTF-8') as german_textfile:
        german_textfile.write(f"{german_word}\n")
    with open(ENGLISH_TEXTFILE, "a", encoding='UTF-8') as english_textfile:
        english_textfile.write(f"{english_word}\n")

def get_random_vocabulary():
    german_words = read_file_as_list(GERMAN_TEXTFILE)
    english_words = read_file_as_list(ENGLISH_TEXTFILE)
    index = random.randint(0, len(german_words)-1)
    return german_words[index], english_words[index]


def main():
    while True:
        while True:
            try:
                menue_choice = int(input("Was möchtest du tun?\n1 - Wörter hinzufügen\n2 - Vokabeln abfragen\n3 - Programm beenden\n>> "))
                if menue_choice not in (1,2,3):
                    raise ValueError
                break
            except ValueError:
                print("Falsche eingabe!")
        if menue_choice == 1:
            english_word = input("Gib das englische Wort ein: ")
            german_word = input("Gib das deutsche Wort ein: ")
            add_words_to_textfiles(german_word, english_word)
            print(f"Die Worte {english_word} und {german_word} wurden hinzugefügt.")
        if menue_choice == 2:
            while True:
                print("Du bekommst deutsche Wörter und musst diese ins englische Wort übersetzen, zum abbrechen 'q' eingeben!")
                german_word, english_word = get_random_vocabulary()
                user_translation = input(f"Englische Übersetzung von {german_word}: ")
                if user_translation == 'q':
                    break
                if user_translation == english_word:
                    print("Korrekt!")
                else:
                    print(f"Falsch! Die richtige Antwort wäre {english_word}")





if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

`Entry` ist ein sehr allgemeiner Begriff, `Vocable` wäre treffender. Die beiden toString-Methoden sind unnötig, da Du gleich auf das Attribut zugreifen könntest. Methoden schreibt man wie Funktionen komplett klein.

Dateien öffnet man innerhalb eines with-Statements. Man gibt immer explizit ein Encoding an. So wie das jetzt dasteht, liest Du nur die erste Zeile der Datei und machst daraus genau eine Vokabel. Wenn Du viele Werte lesen willst, brauchst Du eine Schleife. Zeilen werden immer mit einem Zeileendezeichen abgeschlossen. Wenn Du das nach dem Einlesen nicht mehr möchtest, dann mußt Du es entfernen.
Beim Speichern macht man einfach das Umgekehrte.

Funktionen bekommen immer alles, was sie brauchen als Argument. Es sollte keine globalen Variablen geben. In `eingabe` werden eintraege und speichern einfach so verändert. Wobei speichern ja das selbe enthält wie eintraege, das eine kann also weg.

Statt random.randint wäre random.randrange einfacher, weil es schon die passenden Grenzen hat, noch besser ist aber random.choice, weil es aus einer Liste einen zufälligen Eintrag wählt.
Strings setzt man nicht mit + zusammen, sondern nutzt format-Strings.

Auf oberster Ebene sollte kein ausführbarer Code stehen, sondern nur Funktions-/Klassendefinitionen (und Konstanten). Das Hauptprogramm wird in eine Funktion namens `main` gepackt.

Code: Alles auswählen

import random

class Vocable:
    def __init__(self, deutsch, englisch):
        self.deutsch = deutsch
        self.englisch = englisch

def read_vocabulary():
    with open('deutsch.txt', encoding="utf8") as deutsch, open('englisch.txt', encoding="utf8") as englisch:
        vocabulary = [Vocable(d.strip(),e.strip()) for d, e in zip(deutsch, englisch)]
    return vocabulary
    
def write_vocabulary(vocabulary):
    with open('deutsch.txt', 'w', encoding="utf8") as deutsch, open('englisch.txt', 'w', encoding="utf8") as englisch:
        for vocable in vocabulary:
            deutsch.write(f"{vocable.deutsch}\n")
            englisch.write(f"{vocable.englisch}\n")

def input_vocables():
    new_vocables = []
    while True:
        deutsch = input("Deutsches Wort: ")
        if deutsch == "#":
            break
        englisch = input("Englisches Wort: ")
        if englisch == "#":
            break
        new_vocables.append(Vocable(deutsch, englisch))
    return new_vocables


def train(vocabulary):
    while True:
        vocable = random.choice(vocabulary)
        englisch = input(f"Englische Übersetzung von {vocable.deutsch}: ")
        if englisch == "#":
            break
        if vocable.englisch == englisch:
            print("korrekt")
        else:
            print(f"falsch, richtige Antwort ist: {vocable.englisch}")

def print_all(vocabulary):
    for vocable in vocabulary:
        print(f"{vocable.deutsch} {vocable.englisch}")

def main():
    vocabulary = read_vocabulary()
    while True:
        befehl = input("Befehl:")
        if befehl == "eingabe":
            vocabulary.extend(input_vocables())
        elif befehl == "abfrage":
            train()
        elif befehl == "beenden":
            break
        elif befehl == "ausgabe":
            print_all()
        elif befehl == "hilfe":
            print("Mögliche Befehle:")
            print(" eingabe")
            print(" abfrage")
            print(" beenden")
            print(" ausgabe")
            print(" hilfe")
        else:
            print("Keine bekannte Ausgabe. Für alle Befehle tippe: hilfe.")

    write_vocabulary(vocabulary)
    print("Beenden")

if __name__ == '__main__':
    main()
McAffe
User
Beiträge: 4
Registriert: Mittwoch 30. September 2020, 17:20

Vielen Dank für Eure Antworten, die haben mir sehr geholfen. :D
Wie Ihr vielleicht gemerkt habt ist mein Wissen noch sehr oberflächlich um nicht zu sagen ich bin ein Anfänger.

Nun noch eine Frage, kann man zu dem Code leicht eine Art Erinnerungsfunktion schreiben, die die Häufigkeit speichert wie oft man die
Vokabel richtig oder falsch hatte und somit die Häufigkeit der abfrage dieser Vokabel beeinflusst, oder ist dies sehr aufwendig?
Beziehungsweise wie schreibe ich das dann am besten?

Vielen Dank für Eure Hilfe und beste Grüße.
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

Du hast ja schon eine Klasse, die du einfach um die beiden Zahlen erweitern kannst.
McAffe
User
Beiträge: 4
Registriert: Mittwoch 30. September 2020, 17:20

Sirius3 hat geschrieben: Samstag 3. Oktober 2020, 21:53 Du hast ja schon eine Klasse, die du einfach um die beiden Zahlen erweitern kannst.
Dann würde ich ja eine Variable vergeben, die für jede Vokabel einzelnd auch nach dem schließen gespeichert werden müsste und die die random choice beeinflusst. Diese variable kann ich aber nicht in der Textdatei mit speichern, sonst würde das ja beim Auslesen Schwierigkeiten geben.
Viele Grüße
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum denkst du, dass das nicht geht? So hast du deinen Wunsch geäußert, und so musst du Rea dann halt programmieren, dass du es richtig schreiben und lesen kannst.
McAffe
User
Beiträge: 4
Registriert: Mittwoch 30. September 2020, 17:20

Natürlich geht das irgendwie, nur stehe ich gerade von der Logik wie man das am Sinnvollsten macht auf dem Schlauch, das ist alles.
Antworten