Problem bei der Eingabe von Werten

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
marcel070778
User
Beiträge: 1
Registriert: Donnerstag 20. Oktober 2016, 10:08

Hallo zusammen,
ich sitze seid einiger Zeit an einem Benzinkosten bzw. Benzinverbrauchsrechner. Er funktioniert soweit ganz gut. Allerdings habe ich jetzt einen Bug festgestellt und ich bekomme diesen einfach nicht behoben.

In dem Programm gibt es nach dem Start ein kleines Auswahlmenü. Dort kann man auswählen zwischen Programm beenden, Werte aus Textdatei auslesen und Werte eingeben. Wenn ich Werte eingegeben habe unabhängig davon ob ich diese danach in der erwähnten Textdatei speichere oder nicht, springt das Programm danach wieder in das Menü. Das ist soweit richtig. Wenn ich aber danach wieder den Punkt "Werte eingeben" auswähle um neue Werte einzugeben, werden die vorher eingegeben Werte angezeigt. Ich habe auch keine Möglichkeit, diese zu ändern. Ich habe bereits versucht den Speicher mit dem "del" Befehl zu löschen. Dies hat aber nicht funktioniert. Am einfachsten wäre es, im Programm den Fehler zu beheben. Ich finde diesen Fehler allerdings nicht. Habt Ihr eine Idee woran es liegen könnte? Ich kopiere mal den Quellcode mit hinein.

Code: Alles auswählen

import sys
import time

def stern():
        print("--------------------------------")
        print("*************ENDE*************")
        print("--------------------------------")

fehler1 = 1
fehler2 = 1
fehler3 = 1

#Hauptprogramm
while True:
    # Hauptmenu, Auswahl
    try:
                menu = int(input("Bitte eingeben"
                    "(1: Ende, 2: Werte abfragen, 3: Werte eingeben): "))
    except:
                print("Bitte geben Sie nur Zahlen zwischen 1 - 3 ein!")
                continue

    #Abfragen, anlegen oder Ende

    if menu == 1:       # Auswahlmenu beenden
        stern()
        break

    elif menu == 2:     # Abfrage der eingegeben Werte
        try:
                d = open("Verbrauchstabelle.txt")

        except:
                print("Dateizugriff nicht möglich!")
                sys.exit(0)

        allezeilen = d.readlines()
        d.close

        summe = 0
        for zeile in allezeilen:
            print(zeile, end="")

    elif menu == 3:     # Werte eingeben             
        try:
                d = open ("Verbrauchstabelle.txt","a")

        except:
                	print("Dateizugriff nicht erfolgreich!")

        while fehler1 == 1:
                print("Wieviel KM sind Sie gefahren?")
                x = input()

                try:
                        KM = float(x)
                        fehler1 = 0

                except:
                        print("Bitte geben Sie nur Zahlen ein!")

        while fehler2 == 1:
                print("Wiviel Liter haben Sie getankt?")
                y = input()

                try:
                        Liter = float(y)
                        fehler2 = 0

                except:
                        print("Bitte geben Sie nur Zahlen ein!")

        while fehler3 == 1:
                print("Was kostet der Liter Kraftstoff?")
                z = input()

                try:
                        Preis = float(z)
                        fehler3 = 0

                except:
                        print("Bitte geben Sie nur Zahlen ein!")
        
        Verbrauch = Liter / KM * 100    # Formel zur Berechnung des Benzinverbrauchs
               
        Kosten = Preis * Verbrauch      # Formel zur Berechnung der Kosten pro 100 KM
        
        print("Es ergab sich ein Verbrauch von", round(Verbrauch, 2), "Liter.")
        print("Die Kosten für 100 KM betragen",round(Kosten, 2) , "Euro.")

        
        if      Verbrauch > 8:
                print("Das Auto hat einen hohen Kraftstoffverbrauch")

        elif    Verbrauch < 6:
                print("Das Auto hat einen niedrigen Kraftstoffverbrauch.")

        elif    Verbrauch  > 6 or Verbrauch < 8:
                print("Das Auto hat einen normalen Kraftstoffverbauch")

        

        print("Wenn Sie die Werte speichern möchten, geben Sie 'y' ein")
        x = input()



        if x == "y":    # Eingegebene Werte in Text Datei speichern

                li = [time.strftime("%d.%m.%Y %H:%M:%S"), KM, round(Verbrauch, 2), round(Verbrauch, 2)]
                d.write(str(li[0]).replace (".",".") + "  "       
                                + str(li[1]).replace (".",",") + " gefahrene KM; "
                                + str(li[2]).replace(".",",") + " Liter/100 KM; "
                                + str(li[3]).replace(".",",") + " Euro/100KM \n")
                
        else:
        
                stern()
        d.close
Zuletzt geändert von Anonymous am Donnerstag 20. Oktober 2016, 10:29, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@marcel070778: erst mal allgemein zum Programm:
Eingerückt wird immer mit 4 Leerzeichen pro Ebene und nicht mal 4 mal 8 oder mit Tabs gemischt. Die vielen Leerzeilen an ungewöhnlichen Stellen erschweren das Lesen auch.
Nackte excepts sind böse, weil sie wirklich jeden Fehler verschlucken. Wenn Du eine Zahl konvertieren willst, fang nur den ValueError ab, der auftreten kann, wenn Du Dateien öffnest, nur den IOError, usw.
Funktionen muß man auch aufrufen, d.close(). Am besten verwendest Du aber dort das with-Statement, dann wird die Datei automatisch geschlossen.
Zum "Werte eingeben": Irgendwelche Flags am Anfang des Programms zu setzten um sie dann viel später zu verwenden, machen ein Programm unübersichtlich und damit fehleranfällig. Variablen erst dort initialisieren, wo sie gebraucht werden. Variablen durchzunummerieren ist auch meist keine gute Idee. Namen sollten aussagekräftig sein, eine angehängte 1 ist das nicht. Statt den Fehler-Flags würde sich sowieso eine Endlosschleife mit break besser eignen.
Statt immer wieder den selben Code leicht modifiziert zu kopieren, solltest Du Funktionen benutzen. Das Eingeben eines Wertes wiederholt sich 3 mal. Auch die einzelnen Menü-Punkte gehören zur Übersichtlichkeit in Funktionen.
In Zeile 111 schreibst Du etwas in eine Datei, die schon in Zeile 46 geöffnet wurde. Du bist also ständig am hin und her-scrollen um zu verstehen, was da eigentlich gemacht wird.

Zeile 88: Statt Zahlen mit round für die Ausgabe zu bearbeiten benutzt Du am besten String-Formatierung: "Es ergab sich ein Verbrauch von {0:.2f} Liter.".format(Verbrauch)
Zeile 98: Die Bedingung ist das Gegenteil aller vorangegangenen Bedingungen, ergo ein else-Zweig.
Zeile 110ff: auch hier bietet sich String-Formatierung an. Was ist eigentlich der Sinn, Werte in eine Liste zu packen, um sie gleich danach per Index-Zugriff wieder auszulesen?

Die Lösung Deines konkreten Problems liegt in der Lösung der vielen weiteren Probleme, die ich Dir hier aufgezeigt habe.
BlackJack

@marcel070778: Bei dem Quelltext braucht man IMHO mit der Fehlersuche gar nicht erst anfangen, denn das ist zu viel und zu unübersichtlich.

Teil das am besten erst einmal sinnvoll auf Funktionen auf die jeweils eine Aufgabe lösen. Dann brauchst man sich auch keine Sorgen über irgendwelche Werte von vorherigen Aufrufen machen, denn die lokalen Namen existieren nur während die Funktion läuft.

Auch das Hauptprogramm sollte in einer Funktion stehen, die üblicherweise `main()` genannt wird. Auf Modulebene gehört in der Regel nur Code der Konstanten, Funktionen, und Klassen definiert.

Funktionsnamen sind üblicherweise nach der Tätigkeit benannt, die sie durchführen. Um sie besser von ”passiven” Werten unterscheiden zu können. `stern()` ist keine Tätigkeit. Die Funktion braucht man auch nicht wenn man den Code so strukturiert, dass die drei Zeilen am Ende des Programms ausgeführt werden.

Namen durchnummerieren ist ein „code smell“. Oft will man da stattdessen eine Datenstruktur, meistens eine Liste, verwenden. Bei den `fehler*`-Werten will man aber eher diese Namen komplett loswerden, denn die sind unnötig. Man kann das jeweils genau so lösen wie Du auch die äusserste ``while``-Schleife umgesetzt hast: eine ”Endlosschleife”, die bei Bedarf mit ``break`` verlassen wird.

Die Werte für diese Fehler-Flags sind Wahrheitswerte; da sollte man True und False für verwenden und nicht 1 und 0.

Eingerückt wird per Konvention vier Leerzeichen pro Ebene.

Keine ”nackten” ``except:``\s ohne konkrete Ausnahmen verwenden. Immer nur die Ausnahmen behandeln von denen man sicher weiss, dass die Behandlung die im ``except``-Block steht, auch angemessen und richtig für die jeweilige Ausnahme ist. Sonst handelt man sich schnell mal Fehler ein die nur sehr schwer zu finden sind, weil die nicht sinnvoll behandelt werden, aber auch nicht mehr bis nach oben ”durchschlagen”.

Dateien die man öffnet, sollte man auch wieder schliessen. Du rufst die `close()`-Methode nie auf. Dazu fehlen die Klammern für den Aufruf. Noch besser wäre es aber die ``with``-Anweisung zu verwenden. Dann ist sichergestellt, dass die Datei auch geschlossen wird, wenn zwischen öffnen und schliessen zum Beispiel eine Ausnahme auftritt.

`summe` wird definiert, aber nicht verwendet.

Wenn die Zeilen nur ausgegeben werden, braucht man sie vorher nicht in den Speicher laden. Da kann man auch einfach über das Dateiobjekt iterieren und jede gelesene Zeile sofort ausgeben.

Der Dateiname steht mehr als einmal im Quelltext. Wenn man den mal ändern möchte, müsste man das an jeder Stelle tun und hoffen keine zu vergessen. Wiederholungen in Code und Daten ist etwas was man als Programmierer zu vermeiden versucht. Feste Dateinamen und andere Werte werden deshalb normalerweise als Konstanten definiert.

Dann gibt es *eine* Stelle wo man den Wert für das ganze Programm ändern kann.
Die Fehlerbehandlung beim Eingeben der Werte beim öffnen der Datei ist fehlerhaft. Wenn die Datei nicht geöffnet werden konnte, macht das Programm trotzdem weiter, und wird dann natürlich beim schreiben in die Datei, die nicht geöffnet werden konnte, auf die Nase fallen.

Namen sollten dem Leser vermitteln was der Wert im Kontext des Programms bedeutet. `d`, `x`, `y`, `z` oder `fehler2` tun das nicht. `x`, `y`, und `z` würden das vielleicht tun, wenn es sich um X-, Y- und Z-Koordinaten handeln würde, das ist in diesem Programm aber nicht der Fall. Bei den dreien ist es auch unsinnig drei verschiedene Namen zu verwenden, denn sie werden nicht gleichzeitig verwendet.

`KM` passt als Name nicht so ganz zum Wert. Bei `Verbrauch` und `Preis` hast Du ja auch nicht `Liter` und `Euro` genommen. `KM` hiesse also besser `Weg` oder `Strecke`.

Die drei ``while``-Schleifen bei der Eingabe sind zu ähnlich als das man sie wirklich dreimal hinschreiben sollte. Da kann man die Gemeinsamkeiten in eine Funktion heraus ziehen.

Die `round()`-Funktion ist nur sehr selten wirklich sinnvoll. Für die Präsentation als Zeichenkette kann man die Anzahl der Nachkommastellen über die Formatierungscodes für die `format()`-Methode festlegen.

Bei der Einschätzung des Verbrauchs ist ein Fehler. Es gibt genau zwei Werte für `Verbrauch` bei denen dort *nichts* ausgegeben wird. Das Problem löst sich wenn Du mal über die Sinnhaftigkeit des letzten ``elif`` nachdenkst. Und wann ein ``else``-Zweig ausgeführt wird.

Die Liste `li` macht keinen Sinn und die Werte sind fehlerhaft — zweimal Verbrauch statt Verbrauch und Preis.

Bitte einmal Effekt und Sinn von ``….replace('.', '.')`` erklären. ;-)

Das Dateiformat ist ungünstig zur Datenspeicherung, weil es schwierig ist das wieder einzulesen um mit Daten zu arbeiten. Das hast Du vielleicht selbst schon gemerkt und deswegen `summe` nicht weiter verwendet. Man sollte eine Trennung zwischen interner Datenrepräsentation, Anzeige für den Benutzer, und bei der Speicherung machen. Intern Objekte mit denen man am besten arbeiten kann, für den Benutzer so dass er es gut lesen/verstehen kann, Speicherung so, dass man es leicht wieder einlesen und weiterverarbeiten kann.

Zeitstempel würde man Programmintern beispielsweise als `datetime.datetime`-Objekte verwenden, und Zahlen als `float` oder `decimal.Decimal`. Dem deutschen Benutzer zeigt man Zeitstempel im DD.MM.JJJJ HH:MM:SS-Format und Zahlen mit ',' statt '.'. Speicherung von Zeitstempeln dann eher im ISO-Format und Zahlen wie bei Rechnern üblich mit einem Dezimalpunkt. Und in einem Standardformat strukturiert. Also hier beispielsweise eine CSV-Datei.
Antworten