Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

11.03.2014, 16:12

Problem beim Einlesen einer Binärdatei mit einem ifstream-Objekt

Hallo alle zusammen,

ich habe folgendes Problem:

Ich habe mit meinem Programm in einem vorherigen Durchlauf eine verkettete Liste mit einem ofstream-Objekt
als Binärdatei abgespeichert.
Wenn ich jetzt beim nächsten Programmstart diese Datei mit einem ifstream-Objekt wieder einlesen
will, funktioniert das nicht, das heißt, das Programm stürzt an dieser Stelle einfach ab.
Wenn bisher keine solche Datei existiert, läuft alles problemlos, auch der Fehler wird abgefangen.

Das hier ist die Liste:

Quellcode

1
2
list<CEntry*> lClassList;
list<CEntry*>::iterator i;

Hier wird die Datei gespeichert:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
void CList::SaveList()
{
cout << "[Saving the list.]" << endl;
ofstream Output("Klassenliste.kll", ios::binary);
if(!Output)
    {
cout << "Error: List could not be saved!" << endl;
        return;
}
    Output.write((char*) &lClassList, sizeof (lClassList));
    Output.close();
}

Hier wird die Datei gelesen:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void CList::LoadList()
{
cout << "[Loading the list.]" << endl;
ifstream Input("Klassenliste.kll", ios::binary);
if(!Input)
    {
cout << "Error: List could not be loaded!" << endl;
        return;
}
    while(Input.good())
{
        Input.read((char*) &lClassList, sizeof (lClassList));
    }
Input.close();
}


Hier wäre auch noch die Klasse CEntry, aus deren Objekten die Liste besteht:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef CEINTRAG_H
#define CEINTRAG_H

#include <iostream>
#include <string>

#include "CList.h"
;
using namespace std;   //Bitte keine Kommentare hierzu, ich weiß, dass das nicht gerade optimal ist...

class Clist;           //Ist momentan noch vonnöten, ich werde das noch ändern...

class CEntry
{
public:
    string sFirstName;
    //Und einige andere Strings, der Einfachheit halber weggelassen...
    int EntryNumber;    //Bisher noch ungenutzt...

    CEntry (Clist*);    //Der Zeiger wird im Konstruktor benötigt, sonst nicht...
    void PrintEntry ();
    void PrintName (int);
}
#endif

Die Syntax ist definitiv korrekt, das Problem tritt ja auch erst in der Laufzeit auf.
Ich hab auch schon mal die Fehlerbits abgefragt, konnte jedoch kein Gesetztes entdecken...

Weiß jemand von euch, wodran der Fehler (das typische Fenster mit „Das Programm funktioniert nicht
mehr, wollen sie nach einer Lösung suchen? usw. etc.“) liegen könnte?

Danke!

Grüße,
shaverhund98

Edit: Sorry für die teilweise komische Formatierung des Quellcodes... :D

2

11.03.2014, 17:01

Nun, ich glaube du scheinst C++ mit Java oder ähnlichem zu vergleichen. In C++ musst du jedes einzelne Objekt, das du speichern willst selbst serialisieren. Sprache wie Java oder C++ nehmen das für dich ab, d.h. du kannst dem Stream einfach irgendeine Instanz irgendeiner Klasse übergeben, und er schreibt diese dann. Was du hier allerdings in C++ machst ist nicht ganz richtig. In C++ musst du jedes einzelne Element selbst schreiben, und dann auch selbst wieder einlesen. Du speicherst momentan einfach nur den Zeiger auf deine Liste in die Datei. Da die Liste aber beim nächsten Programmstart woanders liegen wird, ist der Zeiger nicht mehr korrekt, und du greifst auf möglicherweise belegte Speicher zu, verletzt den Speicher, was schlussendlich in einem Absturz endet. Um die Liste wirklich zu speichern, solltest du zuerst die Anzahl der Elemente in der zu speichernden Liste schreiben, und danach die Daten jedes einzelnen Elementes speichern. Dann kannst du zum Einlesen zuerst die Anzahl der Elemente einlesen, und dann diese nutzen, um die gegeben Anzahl an Elementen die noch folgen einzulesen. Angenommen du hast also folgende Liste der Größe 3, die Elemente vom Type int enthält: ( 8, 13, 224 ), dann kannst du sie folgendermaßen schreiben:

(Compiler-ungetestet)

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Die Liste (wir nehmen an, sie sei schon mit Inhalt gefüllt):
std::list<int> liste;
std::ofstream out("data.dat", std::ios::binary);

// Schreibe die Anzahl an Elementen:
out.write(reinterpret_cast<char*>(&liste.size()), sizeof(std::size_t));

// Jetzt schreibe jedes Element:
for (auto it = liste.begin(); it != liste.end(); ++it)
{
    out.write(reinterpret_cast<char*>(&(*it)), sizeof(int));
}

out.close();

// Erneut einlesen:
std::list<int> neueListe;
std::ifstream in("data.dat", std::ios::binary);

// Lese die Anzahl an Elementen:
std::size_t anzahl = 0;
in.read(reinterpret_cast<char*>(&anzahl), sizeof(std::size_t));

// Nun lese die wirklichen Elemente:
for(std::size_t i = 0; i < anzahl; ++i)
{
    int x = 0;
    in.read(reinterpret_cast<char*>(&x), sizeof(int));
    neueListe.push_back(x);
}


Dieser Code sollte eine beliebige Integer-Liste speichern und wieder laden.

Du kannst wie gesagt in C++ nicht einfach x-beliebige Objekte speichern. Du musst C++ schon sagen, WIE du sie speichern möchtest, WAS du speichern möchtest, bei einer eigenen Klasse also z.B. alle Felder, und schließlich musst DU sie selbst speichern. Das ist natürlich ein eindeutiger Mehr an Aufwand, aber in gewissen Fällen hat das auch seine Vorzüge.

Abschließend rate ich dir, dir nochmal File-IO mit C++ anzuschauen. Ganz gute Tutorials gibt's hier: http://www.cprogramming.com/tutorial/lesson10.html

Ich hoffe, dass meine Antwort dir hilft.
Liebe Grüße,
~ EuadeLuxe ~

#EDIT: Um das Ganze noch einmal kurz zu fassen: Du speicherst nur den Ort, an dem deine Liste dieses Mal im Speicher liegt, und nicht den Inhalt der Liste. Nach dem Ende des Programms wird der Inhalt gelöscht, und bei erneutem Start wird die Liste erneut an einem anderen Ort im Speicher erstellt. Du liest jetzt aber von der alten Stelle, bzw. füllst die Liste mit unbrauchbarem Müll, verletzt dabei die Memory-Acces-Rechte, und bringst damit dein Programm zum Absturz.

3

12.03.2014, 19:53

Danke für die schnelle Antwort! :D

Kann gut sein, dass ich das mit Java verwechsle, hab in letzter Zeit nur damit gearbeitet, und in C++ noch kaum Erfahrung.
Ich hab zwar momentan noch ein bisschen am Umschreiben zu knabbern, aber ich versteh jetzt den Fehler...

Danke für deine Hilfe!

Liebe Grüße,
shaverhund98 :)

4

12.03.2014, 21:29

Kein Problem, solang es hilft ;) .

Liebe Grüße,
~ EuadeLuxe ~

Werbeanzeige