Struct in Binärdatei schreiben: Eintrag ist immer doppelt in der Datei vorhanden



  • Hallo zusammen.

    Schreibe gerade an einem Highscore-System für ein Spiel und möchte da eine Struktur in eine Binärdatei schreiben.
    Leider taucht der Eintrag immer doppelt in der Datei auf, wenn ich ihn reinschreibe. Ich komme nicht drauf, woran das liegt..
    Hier der Test-Code:

    #include <iostream>
    #include <fstream>
    
    struct highscoreEntry
    {
    	char name[30];
    	int punkte;	
    };
    
    int main()
    {
    	std::fstream HighscoreFile;
    
    	highscoreEntry Entry;
    	highscoreEntry newEntry;
    
    	//Neuen Eintrag einlesen	
    	std::cout << "Name: ";
    	std::cin >> newEntry.name;
    	std::cout << "Punktzahl: ";
    	std::cin >> newEntry.punkte;
    
    	//Eintrag in Datei schreiben 	
      	HighscoreFile.open("test.dat", std::ios::out | std::ios::binary);
      	HighscoreFile.clear();
    	  HighscoreFile.write((char*)&newEntry, sizeof(newEntry));
      	HighscoreFile.close();
    
      	//Eintrag aus Datei lesen
      	HighscoreFile.open("test.dat", std::ios::in | std::ios::binary);
      	while(!HighscoreFile.std::ios::eof())
    	  	{
    	  		HighscoreFile.read((char*)&Entry, sizeof(Entry));
    	  		std::cout << Entry.name << ": " << Entry.punkte << std::endl;
    	  	};
      	HighscoreFile.close();
    
    	int i;
    	std::cin >> i;
    
    	return 0;
    };
    

    Bei der Eingabe

    Name: Peter
    Punkte: 99

    gibt er mir bei dem Lesen aus der Datei, wo ich die Werte ausgebe, an:

    Peter: 99
    Peter: 99

    Also zwar den richtigen Eintrag, leider aber doppelt.
    Woran liegt das? Passt die größe beim Einlesen nicht? Oder ist schon beim Schreiben ein Fehler?

    MfG
    Hundefutter



  • Du hast beim Einlesen einen kleinen, tückischen Fehler: fstream merkt erst beim zweiten mal Lesen, dass sie das Ende erreicht hat. Du könntest also vor dem "std::cout" in der Schleife noch einmal prüfen, ob die Datei am Ende angekommen ist.

    PS: Du kannst die Datei auch mit if ( HighscoreFile ) testen, dabei werden alle Fehler-Bits überprüft, wozu auch eof gehört.



  • Das Problem ist ja, dass eof erst gesetzt wird, wenn die letzte Operation das Ende erreicht hat. Und da du das zuerst überprüfst, läuft die Schleife noch einmal durch, obwohl das Ende bereits erreicht wurde. Überprüf einfach nach dem read, ob eof gesetzt ist und dann mit break raus. (in einer Endlosschleife).



  • Das ist ja eine gemeine Falle...
    Danke für den Hinweis. Habe es jetzt folgendermaßen gemacht:

    while(!HighscoreFile.std::ios::eof())
              {
                  HighscoreFile.read((char*)&Entry, sizeof(Entry));
                  if (!HighscoreFile.std::ios::eof())
                  {
                      std::cout << Entry.name << ": " << Entry.punkte << std::endl;
                  };
              };
    

    Mit der Abfrage fange ich nun die doppelte Ausgabe ab.
    Später werden es auch mehr Elemente sein und die werden dann in eine Liste gepusht. Sollte jetzt so funktionieren.

    Danke für die Hilfe!

    MfG
    Hundefutter



  • Hundefutter schrieb:

    Das ist ja eine gemeine Falle...

    .. und ich kenne niemanden, der da nicht schon reingetappt wäre. => Mach Dir nichts draus. 😃

    1.)
    Eine Alternative wäre noch

    while(HighscoreFile.read((char*)&Entry, sizeof(Entry))) {
       std::cout << Entry.name << ": " << Entry.punkte << std::endl;
    };
    

    Das klappt, weil

    • read() einen istream& zurückliefert,
    • bei dem es im Falle von eof das fail-Bit setzt und
    • es für iostream einen entsprechenden Konvertierungsoperator (void*) gibt, der fail- und bad-Bit überprüft.

    Letztlich willst Du ja vermutlich den Input auch nicht weiterverarbeiten, wenn der Stream korrupt (failBit oder badBit) ist....

    2.)
    Eine weitere Möglichkeit wäre, den Stream bei Auftreten von eof eine exception werfen zu lassen. Das kann an ihm "beibringen" über

    HighscoreFile.exceptions(ios_base::eofbit);
    

    (ungetestet)

    3.)
    ... und "schließlich bin ich der Meinung, dass Karthago zer ...." äh, dass man nur in besonderen Ausnahmefällen structs binär ablegen sollte. Die Files sind compilerspezifisch (Plattform, Version, Optionen, ...) und damit nur sehr eingeschränkt dauerhaft.

    Gruß,

    Simon2.



  • Danke für die Ergänzungen.

    Ich habe ein Highscore-System, welches eben immer Paare von Name und Punktzahl speichern soll.
    Daher habe ich eben das Ganze mit ner struct gelöst:

    struct highscoreEntry
    {
        char name[30];
        int punkte;    
    };
    

    Wenn man structs nicht binär speichern sollte, wie soll ich es denn alternativ lösen?
    Es muss eben immer ein Name mit einer zugehörigen Punktzahl gespeichert werden.

    MfG
    Hundefutter



  • Simpel kannst du es speichern, indem du stumpf in Textdateien schreibst.
    Je eine Zeile für den Namen und einen für die Punkte. Wenn du alles in eine Zeile schreiben würdest, dürftest du entweder keine Namen mit Leerzeichen nehmen oder müsstest mit Trennzeichen arbeiten. Zwei Zeilen sind da einfacher.

    Exceptions würde ich in dem Fall aber auch nicht verwenden, da diese ja nur in Ausnahmen (wie der Name schon sagt) verwendet werden sollten und das das Ende einer Datei erreicht wird, ist ja nicht wirklich die Ausnahme sondern eher die Regel. 😉



  • Zeilen weise die Scores sichern.

    Datei:

    1. Zeile : Player1 - Score1
    2. Zeile : Player2 - Score2
    ...

    Dann einfach mit getline auslesen.

    Greetz Tobi



  • T0bi schrieb:

    Zeilen weise die Scores sichern.

    Datei:

    1. Zeile : Player1 - Score1
    2. Zeile : Player2 - Score2
    ...

    Dann einfach mit getline auslesen.

    Dann nenn ich mich "Huhn - 9999999" 😃



  • Fellhuhn schrieb:

    ...Exceptions würde ich in dem Fall aber auch nicht verwenden, ...

    Ich auch nicht ... habe es aber "vollständigkeitshalber" aufgenommen.

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Fellhuhn schrieb:

    ...Exceptions würde ich in dem Fall aber auch nicht verwenden, ...

    Ich auch nicht ... habe es aber "vollständigkeitshalber" aufgenommen.

    Hatte ich auch nicht angenommen, wollte nur nicht das sich jemand dazu verleitet fühlt ohne drüber nachzudenken. 😉



  • Fellhuhn schrieb:

    T0bi schrieb:

    Zeilen weise die Scores sichern.

    Datei:

    1. Zeile : Player1 - Score1
    2. Zeile : Player2 - Score2
    ...

    Dann einfach mit getline auslesen.

    Dann nenn ich mich "Huhn - 9999999" 😃

    Was haste davon, wenn du Huhn - 9999999 heißt, dein Score aber bei 0 liegt 😉

    Greetz Tobi



  • Kann ich das Zeilenweise sichern auch in ner Binärdatei machen?
    Möchte nämlich keine Textdatei nehmen, da sonst jeder Hegel die Highscore ganz einfach manipulieren kann...

    MfG
    Hundefutter



  • haeng einfach nen std::ios::binary dran oder nenn die Datei einfach *.bin dann und machs wiw gewohnt mit ner *.txt Datei.

    Greetz Tobi



  • Binär schützt nicht vor Manipulation, auch wenn der Personenkreis der die editieren kann, doch deutlich kleienr sein dürfte als der derer die eine Textdatei editieren können. 😉

    Wenn du es sicherer haben willst, schreib noch eine Checksumme rein oder etwas in der Art. Einfach irgend eine Zahl mit irgend einem Algorithmus anhand des Namens, der Punktezahl und der Position in der Datei plus Gesamtzahl der Einträge und speicher die mit in die Datei und überprüf die beim Laden.



  • Das mit der Checksumme ist natürlich noch etwas sicherer..
    Denke aber, dass ich es erstmal bei einer Binärdatei belasse, das sollte eigentlich Schutz genug sein.. Die Highscore ist ja auch erstmal nur für einen SinglePlayer, also betrügt man sich ja eh nur selbst, wie man so schön sagt 😉



  • T0bi schrieb:

    Was haste davon, wenn du Huhn - 9999999 heisst, dein Score aber bei 0 liegt 😉

    dann steht in einer zeile Huhn - 9999999 - 0

    wenn beim auslesen ueber split gearbeitet wird ( name = eintrag[0]; hiscr = eintrag[1]; ) hat man den falschen Wert 😉



  • Hundefutter schrieb:

    ...Denke aber, dass ich es erstmal bei einer Binärdatei belasse, das sollte eigentlich Schutz genug sein.. Die Highscore ist ja auch erstmal nur für einen SinglePlayer, also betrügt man sich ja eh nur selbst, wie man so schön sagt 😉

    Ich denke, dass falsch einschätzt:
    - welche Probleme Du Dir mit Binärdateien einhandelst (unterschätzt Du)
    - wie "sicher" sie sind (überschätzt Du)
    - wieviel "Sicherheit" Du brauchst (überschätzt Du)
    - wie schwierig eine Checksumme (die mehr Sicherheit bietet als "binär") (überschätzt Du)

    ... aber vllt. bist Du auch einfach an dem Punkt, wo Du denkst "Ist doch egal - Hauptsache, es läuft jetzt endlich". Ich kenne dieses "Abkürzungsschild" und kann Dir sagen: Diese Entscheidung wird Dir eher früher als später in den Hintern beißen. 😉

    Andererseits: Hier geht's auch nur um Highscore-Files eines kleinen selbstprogrammierten Spiels. Wenn die weg/falsch/korrupt sind, fliegt kein AKW auseinander.... vielleicht brauchst Du die Erfahrung ja. 😃

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Ich denke, dass falsch einschätzt:
    - welche Probleme Du Dir mit Binärdateien einhandelst (unterschätzt Du)
    - wie "sicher" sie sind (überschätzt Du)
    - wieviel "Sicherheit" Du brauchst (überschätzt Du)
    - wie schwierig eine Checksumme (die mehr Sicherheit bietet als "binär") (überschätzt Du)

    Ich möchte nur eine kleine Highscore-Liste mit 5-10 Einträgen sichern. Da hier jeder das Spiel normalerweise alleine spielt, muss es nicht unglaublich sicher sein, aber eine Textdatei will ich auch nicht nehmen, weil die gerade zu dazu einläd die Highscore zu manipulieren. Wenn jemand es dann doch umbedingt machen will und die Binärdatei ändert, dann soll er das von mir aus tun. Aber es soll eben nicht zu einfach und offensichtlich sein.

    Simon2 schrieb:

    ... aber vllt. bist Du auch einfach an dem Punkt, wo Du denkst "Ist doch egal - Hauptsache, es läuft jetzt endlich". Ich kenne dieses "Abkürzungsschild" und kann Dir sagen: Diese Entscheidung wird Dir eher früher als später in den Hintern beißen. 😉

    Andererseits: Hier geht's auch nur um Highscore-Files eines kleinen selbstprogrammierten Spiels. Wenn die weg/falsch/korrupt sind, fliegt kein AKW auseinander.... vielleicht brauchst Du die Erfahrung ja. 😃

    Ich arbeite grundsätzlich nicht nach dem Prinzip "hauptsache es läuft"!
    Welche Variante würdest du denn vorschlagen, welche die Manipulation nicht so einfach macht wie eine Textdatei? Ist da eine Binärdatei nicht die logische Konsequenz?

    MfG
    Hundefutter



  • Welche Variante würdest du denn vorschlagen, welche die Manipulation nicht so einfach macht wie eine Textdatei? Ist da eine Binärdatei nicht die logische Konsequenz?

    Du kannst das ganze noch verschlüsseln. Ist aber den Aufwand kaum Wert. Das einfachste wäre wohl das irgendwie zu speichern und vlt. die Zahl ein wenig zu manipulieren, damit man sie nicht so schnell findet. Also zum Highscore noch 5 dazuzählen und dann das ganze durch 2 rechnen. Dann kann man die Zahl nicht mehr (so schnell) mit einen Punktestand in Verbindung bringen.


Log in to reply