C++ structs lesen

Orthak

Mitglied
Hallo,
ich arbeite daran C++ structs in Binärform zu lesen und zu schreiben. Ersteres funktioniert ganz gut, doch das Lesen bereitet mir Probleme.

Folgend meine bisherigen Funktionen:
Code:
writeBinary(QATuple tuple, const char* fileName) {

    fstream binary_file(fileName, ios::out|ios::binary|ios::app);

    if(binary_file == NULL)
        return false;

    binary_file.write(reinterpret_cast<char*>(&tuple),sizeof(QATuple));
    binary_file.close();

    printf("Objektadresse: %p | Dateiname: %s\n", &tuple, fileName);
    return true;
}

readBinary(const char* fileName) {

    QATuple p_Data;
    fstream binary_file(fileName,ios::binary|ios::in);

    if(binary_file == NULL)
        return false;

    binary_file.read(reinterpret_cast<char*>(&p_Data),sizeof(QATuple));
    binary_file.close();

    printf("Frage: %s\n", p_Data.question.c_str());
    return true;
}

So lese ich allerdings immer nur ein struct aus.
Ich denke am sinvollsten ist es den Lesezeiger jedes Mal um sizeof(QATuple) zu verschieben bis Dateiende. Jedes mal wenn dieser Vorgang abgeschlossen wird, wird das Ergebnis in zB. einen Vektor geschrieben und dieser dann zurückgegeben.

Leider gelingt es mir nicht wirklich den Zeiger entsprechend zu verschieben. Hat jemand eine Idee wie man das (so oder auch anders) realisieren könnte?

P.S.: Code der struct:
Code:
typedef struct questionAnswersTuple {
	string question;
	string answer;
	string wrongAnswers[3];
} QATuple;
 
Hi.
ich arbeite daran C++ structs in Binärform zu lesen und zu schreiben. Ersteres funktioniert ganz gut, doch das Lesen bereitet mir Probleme.
Du widersprichst dir irgendwie selbst. Wobei hast du denn nun Probleme?
Ich denke am sinvollsten ist es den Lesezeiger jedes Mal um sizeof(QATuple) zu verschieben bis Dateiende. Jedes mal wenn dieser Vorgang abgeschlossen wird, wird das Ergebnis in zB. einen Vektor geschrieben und dieser dann zurückgegeben.
Meiner Meinung nach wäre es am sinnvollsten die Datei nicht ständig zu öffnen und zu schließen.

Öffne die Datei zu Beginn des Programms, lies die Daten in einen std::vector ein, manipuliere die Daten im Vektor und speichere den Vektor am Ende wieder in der Datei ab.

Gruß
 
Bist du dir sicher, das das schrieben funktioniert?
Das Problem liegt vermutlich an der Klasse "string" und wie sie intern aufgebaut ist.
Ich versuche dir mal das Problm anhand eines anderen Beispiel zu erklären.
angenommen du hast folgendes Struct:
Code:
typdec struct a{
float *ptr;
} A;
Würdest du dieses Struct "Binär" schireben, würde nicht eine FLießkomma Zahl geschrieben, werden, sondern die Adresse wo sich der Zeiger befindet.

Un genau das ist auch das Problem bei "string" intern arbeiten diese mit Zeigern, weshalb du Speicheraddressen in deine Datei schreibst. Beim auslesen sind diese Adressen leiderwertlos.

mfg
Gene
 
Du widersprichst dir irgendwie selbst. Wobei hast du denn nun Probleme?
Stimmt, das klingt irgendwie unlogisch. Letzteres wäre das richtige Wort gewesen :rolleyes: .

lies die Daten in einen std::vector ein
Genau dort liegt mein Problem. Wie genau setze ich das am besten um? Meine bisherigen Versuche waren leider nicht sehr erfolgreich. Ist mein bisheriger Ansatz (um sizeof(QATuple) weiterspringen) überhaupt praktikabel?

Bist du dir sicher, das das schrieben funktioniert?
Auch wenn ich mich mit dem Aufbau der string Klasse nicht so gut auskenne, hatte ich selbige Befürchtung. Erstaunlicherweise klappt es ganz gut. Die Zeile
Code:
printf("Frage: %s\n", p_Data.question.c_str());
liefert wie erhoft den geschriebenen Wert der ersten struct (siehe Ende von readBinary).
 
Hmm dann beende bitte mal dein Programm oder leer zwischendurch mal deinen Speicher ;) Das Funktioniert nur weil zufällig noch die alte Speicheradresse nicht übershrieben wurde und du somit glück hast und deine Zeiger szs. noch "gültig" sind, auch wenn dies in keinem Std. steht dass das so sein soll/muss.
 
Hmm dann beende bitte mal dein Programm oder leer zwischendurch mal deinen Speicher
Schade, das Program muss sich die Daten wirklich irgendwo aus dem Speicher geholt haben, denn nach einem Neustart funktioniert es tatsächlich nicht mehr :(.

Unabhängig davon habe ich die Funktionen soweit umgebaut das sie vektorweise Daten in Binärform lesen und schreiben können. Hier einmal das komplette Listing:

C++:
#include <iostream>
#include <fstream>
#include <vector>

using namespace std;

// Variablen
typedef struct qaTuple {
	char question[10];
	char answer[10];
} Tuple;

vector<Tuple> paare;

// Schreiben
bool writeBinary(vector<Tuple> paare, const char* fileName) {

	// Stream fuer binaeres Schreiben
    fstream binary_file(fileName, ios::out|ios::binary|ios::app);

    // Fehler, abbruch
    if(binary_file == NULL)
		return false;

	vector<Tuple>::iterator I = paare.begin();
	while(I != paare.end()) {
		binary_file.write(reinterpret_cast<char*>(&(*I)),sizeof(Tuple));
		printf("Objektadresse: %p | Dateiname: %s\n", &(*I), fileName);
		++I;
	}

        binary_file.close();
	return true;
}

// Lesen
vector<Tuple> readBinary(const char* fileName) {

	Tuple p_Data;
	vector<Tuple> filePaare;
	fstream binary_file(fileName, ios::binary|ios::in);

    if(binary_file == NULL)
		return filePaare;

    while(!binary_file.eof()) {
    	binary_file.read(reinterpret_cast<char*>(&p_Data),sizeof(Tuple));
    	filePaare.push_back(p_Data);
    }

	binary_file.close();
	filePaare.pop_back();
	
	return filePaare;
}


int main() {

	Tuple t1 = {
			"frage1",
			"antwort1"
	}; paare.push_back(t1);

	Tuple t2 = {
			"frage2",
			"antwort2"
	}; paare.push_back(t2);

	Tuple t3 = {
			"frage3",
			"antwort3"
	}; paare.push_back(t3);

	writeBinary(paare, "test.dat");

	vector<Tuple> gelPaare = readBinary("test.dat");
	vector<Tuple>::iterator I = gelPaare.begin();
	printf("Anzahl der Paare: %i\n\n", gelPaare.size());
	int counter = 1;
	while(I != gelPaare.end()) {
		printf("%i: Frage: %s | Antwort: %s\n", counter, (*I).question, (*I).answer);
		++I;
		counter++;
	}

	return 1;
}
Zeile 52 macht mir noch etwas Sorgen. Doch ohne sie ist das letzte struct doppelt enthalten.

In dieser Testanwendung habe ich allerdings auf strings in der struct verzichtet. Hat vielleicht Jemand einen Tip wie ich diesen Makel noch beheben könnte?
 
Zuletzt bearbeitet von einem Moderator:
Zeile 52 macht mir noch etwas Sorgen. Doch ohne sie ist das letzte struct doppelt enthalten.
Ja, das hatten wir doch schonmal... ;-]

Warum verwendest du denn .eof()? :confused:

\edit: Sorry, hab dich verwechselt. Aber zu dem Problem mit eof haben wir hier wirklich sehr viele Beiträge. Zuletzt in einem mit Roncalli. :google:
In dieser Testanwendung habe ich allerdings auf strings in der struct verzichtet. Hat vielleicht Jemand einen Tip wie ich diesen Makel noch beheben könnte?
Du müßtest die Daten serialisieren/deserialisieren. Die Strings könntest du z.B. serialisieren, indem du die Größe (als int) in die Datei schreibst, gefolgt von den Daten. Beim Auslesen liest du erstmal die Größe, so das du weißt wieviel Speicher du allokieren mußt. Dann kannst du den String direkt einlesen usw.

Allerdings ist es da schöner (und portabler) die Struktur einfach in einem Textformat zu speichern. Das kann man mit einem Editor bearbeiten und die Struktur ist im Zweifelsfall einfacher zu ändern.

Gruß
 
Zuletzt bearbeitet:
Danke für den Tip bezüglich eof(). Ich habe die Funktion bisher auch immer sorglos in Schleifenbedingungen verwendet.

Ich habe für mein Problem nun eine praktikable Lösung gefunden. Sie nimmt Vektoren mit den gewünschten structs auf, konvertiert und schreibt sie binär bzw. ließt und konvertiert zurück.

Verwendete structs:
C++:
	// QATuple Konstrukt
	typedef struct questionAnswersTuple {
		string question;
		string answer;
		string wrongAnswers[3];
	} QATuple;

	// QATuple Konstrukt (Binaeraustausch)
	typedef struct binaryTuple {
		char question[100];
		char answer[100];
		char wrongAnswer1[100];
		char wrongAnswer2[100];
		char wrongAnswer3[100];
	} BTuple;

Die eigentlichen Funktionen:
C++:
bool writeBinary(vector<QATuple>* paare, const char* fileName) {

	// Stream fuer binaeres Schreiben
    fstream binary_file(fileName, ios::out|ios::binary|ios::app);

	// Exception werfen bei Fehler
	if(!binary_file.is_open()) {
		string errorMsg = fileName;
		errorMsg += " konnte nicht geoeffnet werden!\n";
		throw (string)errorMsg;
	}

	for(unsigned int i = 0; i < paare->size(); i++) {

		BTuple tmp;
		sprintf(tmp.question, "%s", paare->at(i).question.c_str());
		sprintf(tmp.answer, "%s", paare->at(i).answer.c_str());
		sprintf(tmp.wrongAnswer1, "%s", paare->at(i).wrongAnswers[0].c_str());
		sprintf(tmp.wrongAnswer2, "%s", paare->at(i).wrongAnswers[1].c_str());
		sprintf(tmp.wrongAnswer3, "%s", paare->at(i).wrongAnswers[2].c_str());

		binary_file.write(reinterpret_cast<char*>(&tmp),sizeof(tmp));
	}

    binary_file.close();
	return true;
}

vector<QATuple> readBinary(const char* fileName) {

	BTuple tmpB;
	QATuple tmpS;
	vector<QATuple> paare;

	fstream binary_file(fileName, ios::binary|ios::in);

	// Exception werfen bei Fehler
	if(!binary_file.is_open()) {
		string errorMsg = fileName;
		errorMsg += " konnte nicht geoeffnet werden!\n";
		throw (string)errorMsg;
	}

    while(binary_file.read(reinterpret_cast<char*>(&tmpB),sizeof(tmpB))) {
    	tmpS.question = tmpB.question;
    	tmpS.answer = tmpB.answer;
    	tmpS.wrongAnswers[0] = tmpB.wrongAnswer1;
    	tmpS.wrongAnswers[1] = tmpB.wrongAnswer2;
    	tmpS.wrongAnswers[2] = tmpB.wrongAnswer3;
    	paare.push_back(tmpS);
    }

	binary_file.close();
	return paare;
}
Man muss natürlich aufpassen das die strings nicht über 100 Zeichen haben, bzw. dann die Austauschstruktur anpassen.

Vielen Dank für die ganzen hilfreichen Antworten!
 
Zuletzt bearbeitet von einem Moderator:

Neue Beiträge

Zurück