Seite 1 von 1

Json Format richtig benutzen

Verfasst: Donnerstag 21. April 2022, 13:36
von marvin_r
Hallo zusammen.

Ich bin neu hier und grüße erstmal freundlich in die Runde!
Erstmal möchte ich ein Danke an die Community schicken, ich hab hier beim lesen schon so einiges lernen können.
Leider konnte ich keinen passenden Beitrag zu meinem Problem finden.

Als angehender FISI (Umschulung 8er Monat) bin ich leider nur durch einen Crashkurs in der Programmierung gelaufen.

Ich versuche gerade ein Programm zu schreiben um die durchschnittlichen Zeiten der Pings festzuhalten was auch an sich erstmal funktioniert.
Nun habe ich aber das Problem, dass ich nachdem ich alles in json_files abgelegt habe, beim erneuten Ausführen die Formatierung im json_file nicht mehr passt.
Ich lasse mal den Code und alles was so rauskommt hier und hoffe auf etwas Unterstützung wo ich den Denkfehler habe.

Fehler die durch eine falsche Eingabe der Nutzer entstehen können, bitte ich erstmal zu ignorieren.
Später sollen die Daten nicht mehr händisch angegeben werden bzw. Fehler abgefangen werden.

Ich weiß solche Programme existieren schon, aber ich möchte mich zu Lehrzwecken daran versuchen.
Mit .txt und mit .csv lief ich in ähnliche Fehler.

#Progammanfang....

Code: Alles auswählen

import json, sys, datetime

ping = 0
durchschnitt = 0

pings = open('pings.json','r')
datum_pings = open('datum_pings.json','r')

pings = list(pings)
datum_pings = list(datum_pings)

while True:
    print(pings)
    print(datum_pings)

    datum_pings.append(datetime.datetime.now())

    with open('datum_pings.json', 'w') as datei_1:
        datei = json.dumps(str(datum_pings))
        datei_1.write(datei)
        datei_1.close()


    ping = int(input("Bitte Messwert eingeben\n>"))
    pings.append(ping)

    with open("pings.json", 'w') as datei_2:
        datei = json.dumps(pings)
        datei_2.write(datei)
        datei_2.close()

    print()

    for i in datum_pings:
        print("Datum der Messung", str(i))
        
    for i in pings:
        print("Dauer des Pings:" + str(i))
    
    durchschnitt = int(sum(pings)) / int(len(pings))
    print("Durchschnitt der Dauer eines Pings:", str(durchschnitt))
    input("Enter zum fortfahren!")

#...Programmende


Ausgabe 1. Ausführung
[]
[]
Bitte Messwert eingeben
>10

Datum der Messung 2022-04-21 13:57:29.756469
Dauer des Pings:10
Durchschnitt der Dauer eines Pings: 10.0
Enter zum fortfahren!
[10]
[datetime.datetime(2022, 4, 21, 13, 57, 29, 756469)]
Bitte Messwert eingeben
>20

Datum der Messung 2022-04-21 13:57:29.756469
Datum der Messung 2022-04-21 13:57:34.549347
Dauer des Pings:10
Dauer des Pings:20
Durchschnitt der Dauer eines Pings: 15.0
Enter zum fortfahren!
[10, 20]
[datetime.datetime(2022, 4, 21, 13, 57, 29, 756469), datetime.datetime(2022, 4, 21, 13, 57, 34, 549347)]
Bitte Messwert eingeben
>5

Datum der Messung 2022-04-21 13:57:29.756469
Datum der Messung 2022-04-21 13:57:34.549347
Datum der Messung 2022-04-21 13:57:38.460024
Dauer des Pings:10
Dauer des Pings:20
Dauer des Pings:5
Durchschnitt der Dauer eines Pings: 11.666666666666666
Enter zum fortfahren!


json_files nach dem 1. Durchlauf:

pings.json: [10, 20, 5]
datum_pings.json:"[datetime.datetime(2022, 4, 21, 13, 57, 29, 756469), datetime.datetime(2022, 4, 21, 13, 57, 34, 549347), datetime.datetime(2022, 4, 21, 13, 57, 38, 460024)]"

Soweit so gut, aber beim erneuten Aufrufen des Programms passiert dann folgendes:


Ausgabe 2. Ausführung

['[10, 20, 5]']
['"[datetime.datetime(2022, 4, 21, 13, 57, 29, 756469), datetime.datetime(2022, 4, 21, 13, 57, 34, 549347), datetime.datetime(2022, 4, 21, 13, 57, 38, 460024)]"']
Bitte Messwert eingeben
>1

Datum der Messung "[datetime.datetime(2022, 4, 21, 13, 57, 29, 756469), datetime.datetime(2022, 4, 21, 13, 57, 34, 549347), datetime.datetime(2022, 4, 21, 13, 57, 38, 460024)]"
Datum der Messung 2022-04-21 14:02:25.735320
Dauer des Pings:[10, 20, 5]
Dauer des Pings:1
Traceback (most recent call last):
File "C:\Users\mreese\Desktop\PingMessung\DurchschnittlicherPing.py", line 41, in <module>
durchschnitt = int(sum(pings)) / int(len(pings))
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>>

#....Ausgabenende


json_files nach dem 2. Durchlauf:

pings.json: ["[10, 20, 5]", 1]

datum_pings.json:"['\"[datetime.datetime(2022, 4, 21, 13, 57, 29, 756469), datetime.datetime(2022, 4, 21, 13, 57, 34, 549347), datetime.datetime(2022, 4, 21, 13, 57, 38, 460024)]\"', datetime.datetime(2022, 4, 21, 14, 2, 25, 735320)]"

Ab hier sind dann die json_files dann hinüber, irgendwie packt er meine Liste nicht mehr als Array allein in die files sondern ein Array in ein Array.
Wie kann ich das vermeiden?

Das Array im Array macht mir Schwierigkeiten, ich verstehe nicht genau warum das so passiert.

Ich hoffe der Code ist lesbar, meine Erfahrungen sind leider sehr beschränkt und der Unterricht war aufgrund von Corona nur online und zu wenig.
Vielen Dank schonmal im Vorraus für eure Mühe.

Re: Json Format richtig benutzen

Verfasst: Donnerstag 21. April 2022, 15:44
von derElch
Am Anfang hat du keine keine Datei. Daher ist das auch leer (siehe 1. Ausführung).
Beim 2. Mal steht aber auch schon etwas in der Datei. Diese Liste liest er aber nur als String ein. Siehe deine Ausgabe unter:

Code: Alles auswählen

pings.json: ["[10, 20, 5]", 1]
Das ist eine Liste aus 2 Typen: Ein String der aussieht wie einer Liste und das nächste Element ist ein Integer mit dem Wert 1.
Du musst zuvor die Elemente aus der JSON-Datei in eine passende variable reinladen. Bitte auch hier mit dem with-Statement.

Wenn du das Problem mit dem pings hast, wird dir nämlich auch der Teil mit dem Datum ebenfalls entsprechende Fehler werfen.

Abgesehen einige Ideen / Ansaätze:
Wenn ich einen Buchstaben eingebe wirft das Programm eine Exception / Fehler
Wenn du beim Schreiben mit with arbeitest, kannst du dir das .close() sparen, dafür ist das with-Statement da.
Wieso speicherst du die jeweiligen Werte in zwei unterschiedlichen Dateien?
In der Zeile Durchschnitt: die Konvertierungen zu int sind hier unnötig.
Bei print-Durchschnitt, schau dir bitte f-format an: https://realpython.com/python-f-strings/
Wie brichst du das Programm ab? Mit Strg-C?

Re: Json Format richtig benutzen

Verfasst: Donnerstag 21. April 2022, 16:07
von Sirius3
Pro Import eine eigene Zeile. `sys` wird importiert, aber gar nicht benutzt.
Man initialisiert keine Variablen auf Vorrat. Sowohl ping als auch durchschnitt werden mit 0 initalisiert, diese Werte werden aber nie benutzt.
Dateien, die man öffnet, sollte man auch wieder schließen. Am besten, indem man ein with-Statement benutzt.
Datum und Messwert gehören zusammen. Das sollte nicht in zwei separaten Datenstrukturen stehen, erst recht nicht in zwei unterschiedlichen Dateien.
Du liest kein json ein, sondern die Datei als Strings.
Datum kann nicht direkt als JSON geschrieben werden, das muß man also erst in Strings umwandeln und danach wieder zurückwandeln. Du schreibst aber nicht einmal ein Datum, sondern wandelst die gesamte Liste in seine Stringrepräsentation, und den encodierst Du dann als JSON.
Json-Dateien sind immer utf8-kodiert, Du mußt dieses Encoding beim Öffnen der Dateien angeben.
`i` ist ein schlechter Variablenname, sowohl für ein Datum als auch für eine Dauer.
Sowohl sum als auch len liefert in Deinem Fall den Datentyp int, das nochmal umzuwandeln ist unnötig.

Code: Alles auswählen

import json
import datetime
import dateutil.parser

def read_json(filename):
    with open(filename, encoding="utf8") as file:
        data = json.load(file)
    return [
        (dateutil.parser.isoparse(date), duration)
        for date, duration in data
    ]


def write_json(filename, data):
    data = [
        (date.isoformat(), duration)
        for date, duration in data
    ]
    with open(filename, "w", encoding="utf8") as file:
        json.dump(data, file)


def main():
    durations = read_json("pings.json")
    while True:
        print(durations)
        date = datetime.datetime.now()
        ping = int(input("Bitte Messwert eingeben\n>"))
        durations.append((date, ping))
        write_json("pings.json", durations)
        print()
        for date, duration in durations:
            print(f"Datum der Messung: {date}")
            print(f"Dauer des Pings: {duration}")

        durchschnitt = sum(duration for  date, duration in durations) / len(durations)
        print(f"Durchschnitt der Dauer eines Pings: {durchschnitt:.2f}")
        _ = input("Enter zum fortfahren!")

if __name__ == "__main__":
    main()

Re: Json Format richtig benutzen

Verfasst: Donnerstag 21. April 2022, 16:30
von __blackjack__
@marvin_r: `sys` wird importiert aber nirgends verwendet.

`ping` und `durchschnitt` werden am Anfang an den Wert 0 gebunden, der dann aber nirgends verwendet wird. Diese Zuweisungen machen also keinen Sinn und können weg.

Die Namen `pings` und `datum_pings` werden mal an Dateiobjekte und mal an Listen gebunden. Das ist verwirrend. Auch wenn die Programmiersprache das erlaubt, ist es sehr verwirrend für den Leser.

Dateien die man öffnet, sollte man auch wieder schliessen. Am besten in dem man die ``with``-Anweisung verwendet. Beim schreiben der Dateien machst Du das ja richtig.

Bei Textdateien sollte man immer die Kodierung beim öffnen angeben. Bei JSON-Dateien ist das UTF-8.

Du liest die JSON-Dateien ein als wären es einfach Textdateien, also die Zeilen einfach in eine Liste, statt das JSON-Format sinnvoll zu deserialisieren.

JSON kennt keinen Datumstyp. Diese Werte musst Du beim einlesen und schreiben selbst (de)serialisieren wenn Du `datetime`-Objekte im Programm verwenden willst. Wenn es reicht das als Zeichenkette zu haben, reicht es in den Datenstrukturen gleich eine Zeichenkette hinzuzufügen.

Zusammengehörende Werte verteilt man schon innerhalb von einem Programm nicht auf zwei Listen, und dann schon gar nicht auf zwei Dateien.

Das Ergebnis von `json.dumps()` ist eine Zeichenkette und keine Datei, der Name `datei` ist also sehr irreführend. Es wäre auch einfacher `json.dump()` zu verwenden und das nicht selbst nachzuprogrammieren.

Man nummeriert auch keine Namen durch. `datei_1` und `datei_2` existieren nicht gleichzeitig, also kann man das einfach beide male `datei` nennen. Aber wie gesagt, es sollte sowieso nur eine Datei existieren.

`i` ist ein ganz schlechter Name für etwas anderes als eine ganze Zahl. Insbesondere wenn es die Laufvariable in einer Schleife ist.

Literale Werte wie Dateinamen sollte man nicht mehrfach im Quelltext wiederholen. Das ist fehleranfällig und macht unnötig Arbeit bei Änderungen.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import datetime
import json

JSON_FILENAME = "pings.json"


def main():
    try:
        with open(JSON_FILENAME, encoding="utf-8") as file:
            pings = json.load(file)
    except FileNotFoundError:
        pings = []

    while True:
        print(pings)

        duration = int(input("Bitte Messwert eingeben\n>"))
        pings.append([str(datetime.datetime.now()), duration])

        with open(JSON_FILENAME, "w", encoding="utf-8") as file:
            json.dump(pings, file)

        print()

        for timestamp, duration in pings:
            print(
                f"Datum der Messung: {timestamp}  Dauer des Pings: {duration}"
            )

        durchschnitt = sum(pings) / len(pings)
        print(f"Durchschnitt der Dauer eines Pings: {durchschnitt:.3f}")
        input("Enter zum fortfahren!")


if __name__ == "__main__":
    main()
JSON eignet sich nicht so besonders gut für Daten, die fortlaufend erweitert werden. Und die Daten die hier erfasst werden, sind simpel genug für das CSV-Format.

Re: Json Format richtig benutzen

Verfasst: Donnerstag 21. April 2022, 16:55
von marvin_r
Vielen Dank für das Ausführliche Feedback und den Code den ich jetzt gleich mal genau anschauen werde, damit hatte ich nicht gerechnet.
Ich werde versuchen mal alles so gut wie möglich zu erklären.

Dass ich so nicht das json bekomme war mir nicht ganz klar.
Asche über mein Haupt, da muss ich mir nochmal anschauen wo der unterschied zu with open ist...

sys habe ich importiert um später per sys.exit() das Programm zu schließen.

Die Variablen habe ich zu Anfangs deklariert um sie zu initialisieren, damit man damit arbeiten kann, dass man dies so nicht macht wusste ich nicht.

Ping habe ich da benutzt um es in pings zu schreiben.
ping = int(input("Bitte Messwert eingeben\n>"))
pings.append(ping)
Hier benutze ich durchschnitt am ende des Codes, um mir den Wert per print anzeigen zu lassen.
durchschnitt = int(sum(pings)) / int(len(pings))

oder verstehe ich das Falsch...soll ich die da in der Zeile erst initialisieren?
Dann würde sie ja beim nächsten Durchlauf wieder auf default gesetzt

In zwei verschiedene Dateien habe ich erstmal geschrieben um einfacher mit dem mir neuen json umgehen zu können. sum aus Datum und Pingzeit machte für mich keinen Sinn.

Ich dachte es ist einfacher als *Anfänger* das erstmal zu trennen.
Mit unserem *Lehrer* haben wir immer nur einzelne kleine Funktionen geschrieben und verschiedene Module wie numpy, pandas, sqlite usw getestet.
Es wurde alles sehr Flach behandelt und ich habe das meiste aus dem Schulbuch dem Forum und anderen Internetseiten nebenbei gelernt, laut Lehrplan der Schule wars das mit Programmierung.....ich möchte aber gern später damit meine Arbeit automatisieren wo es geht.

Das mit dem Datum hab ich mir gedacht und das war als String erstmal so in *Ordnung*, wollte das erst an der Pingzeit zum laufen bringen.

heute = datetime.datetime.now()
datum = heute.strftime("%d/%m/%Y %H:%M:%S")

So formatiert wollte ich das Datum dann später ablegen.

Ich hoffe es ist nachvollziehbar.

i dann j usw um die Schleifen zu durchlaufen dachte ich wäre Standard, meine Idee war das len(Liste) halt auch gleich herhalten kann als Wert durch den die Summe geteilt wird um den Durchschnitt zu erhalten.
Sonst benenne ich Variablen auch immer sprechend.
Lernen ich noch muss viel oder so ähnlich.

Leider hab ich für Python immer nur nebenbei noch Zeit und hab halt noch viel Stuff für die Systemadministration und BUCHHALTUNG zu lernen... letzteres würde ich gern den Buchhaltern überlassen.

Super Community, bisschen streng aber sehr hilfsbereit und hilfreich (;
Hatte bisschen angst den Code offen zu stellen.

Vielen lieben Dank an euch und dann werde ich mal wieder ran.

Re: Json Format richtig benutzen

Verfasst: Donnerstag 21. April 2022, 18:19
von Sirius3
`sys.exit` benutzt man nicht, das Programm endet, wenn die `main`-Funktion verlassen wird, z.B. indem sie an ihr natürliches Ende kommt, oder per `return` verlassen wird.

Niemand hat gesagt, dass Du die Summe über das Datum bilden sollst, wie man die Summe aus Einzelnwerten einer komplexeren Struktur bildet, habe ich ja in meinem Code gezeigt.
Das Datum legt man am besten im ISO-Format ab, das ist international eindeutig definiert.
`i` und `j` verbindet der Leser mit einem Index, also eine ganzen Zahl. Wenn das statt dessen ein Datum ist, ist das sehr verwirrend.