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

04.06.2014, 23:27

[c++] | fstream - Problem beim laden (speichern) von Vektoren

Hey Leute,

ich versuche gerade noch rechtzeitig zur WM ein kleines TIPP Spiel auf die Beine zu bekommen. (Und natürlich um meine C++ Kenntnisse zu vertiefen :) )

Um die Bedienung möglichst einfach zu halten möchte ich vorher schon ein paar Eingangsdaten (Welche Länder spielen) als Vektor speicher (kleines Extra Programm), dass später einfach eingelesen werden soll.

Könnte das Problem an dieser Stelle umgehen, da jeder selbst die entsprechenden Daten eingeben kann, es geht aber hauptsächlich um das einlesen von Daten.

Zunächst ein paar Hintergrund Infos:

Die Struktur, die im Vektor gespeichert wird:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
//Struktur Daten erzeugen
struct sTeam
{
    bool     blActive;
    String  strName;
    Text            txtName;
    String  strGroup;
        // + ein paar zusätzliche Daten
};

std::vector<sTeam> vecTeams;        // Vektor, der am Ende gespeichert werden soll


Initialisierung d. Vektors (Anfangsdaten festgelegen)

C-/C++-Quelltext

1
2
3
4
5
6
7
8
for(int i=0; i < (16+(blMode)*16); i++)
{
    sTeam team;
    vecTeams.push_back(team);
    vecTeams[i].blActive   = true;
    vecTeams[i].strName  = "";
        // + ein paar weitere Anfangsdaten
} // ende if


Das ganze soll nun gespeichert werden (in einem Unterverzeichnis "tm")

C-/C++-Quelltext

1
2
3
std::ofstream save("tm/test.dat", std::ios::binary);
save.write((char*) &vecTeams, sizeof(vecTeams));
save.close();


Es wird auch eine Datei erzeugt!

Da das Einlesen in den anderen Programm nicht funktioniert hat, habe ich versucht im selben Programm meinen Code zu prüfen.
Dazu habe ich einen neuen Vektor angelegt und ihn mit anderen Daten initialisiert.

Danach versuche ich die Datei zu öffnen und prüfe ein paar Daten - Es werden die richtigen Daten aus der gespeicherten Datei ausgegeben.

C-/C++-Quelltext

1
2
3
4
5
if (load.is_open() == false)
    { std::cout << "nicht geladen" << std::endl; }

load.read((char*) &vec, sizeof(vec));
load.close();


Wenn das Programm aber durch ist kommt folgende Meldung:
Unbehandelte Ausnahme ...: Zugriffsverletzung beim Schreiben an Position 0xfeeefeee

Nach meiner suche, habe ich eine Ahnung, dass es irgendetwas damit zu tun hat, dass man in der Datei die Adresse übergibt, wo der Vektor gespeichert ist. Diese kann sich wohl ändern und dann natürlich nicht mehr darauf zugegriffen werden.
Dass ich dann dennoch die richtigen Daten erhalte, liegt dann wohl daran, dass der PC noch nicht die Gelegenheit hatte den entsprechenden Speicherbereich zu überschreiben.

Was kann ich nun tun, damit dieser Fehler nicht auftritt, bzw. dass ich die Daten ordentlich speichern kann und später wieder abrufe.
Wie gesagt ich möchte später den Spielstand (Klasse) abspeichern, und da wäre der Effekt ja blöd.

Vielen Dank schon mal fürs durchlesen.
VG

2

05.06.2014, 00:08

C-/C++-Quelltext

1
save.write((char*) &vecTeams, sizeof(vecTeams));

wtf? xD
Egal. Ein besonders schöner Weg wäre es, wenn du dir einen Freund machst.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <ostream>

struct sTeam
{
       friend std::ostream& operator<<(std::ostream& os, const sTeam& team);
       bool blActive;
        // Daten
};

std::ostream& operator<<(std::ostream& os, const sTeam& team)
{
     return os << team.blActive << " " << team.//blablaDatenUndSo
}

Damit sparst du dir so dermaßen viel Schreibarbeit. Das eine Leerzeichen ist nicht umsonst da, dadurch wirst du die Daten später wunderbar per stringstream einlesen können.
Jetzt speicherst du also einfach den ganzen Container, vllt in etwa so:

C-/C++-Quelltext

1
2
3
4
for(auto &a : vecTeams)
{
    save << a << "\n";
}

Das könnte man auch kürzer fassen:

C-/C++-Quelltext

1
2
std::ostream_iterator<sTeam> it(save, "\n");
std::copy(vecTeams.begin(), vecTeams.end(), it);

Wenn du den ostream erstellst und die Datei beschreiben willst, wirst du sicherlich eine leere Datei haben wollen. Also einfach std::ofstream save("tm/test.dat", std::ios::binary|std::ios::trunc);
Und das wars schon mit 'm beschreiben.

Auslesen tust du sie dann bitte nicht mit read, schon gar nicht so abgespacet wie hier load.read((char*) &vec, sizeof(vec)); :P :D
Erstmal wieder zurück zur Struktur sTeam. Hier ist es nun sinnvoll, auch einen >> Operator einzufügen.

C-/C++-Quelltext

1
2
3
4
friend std::istream& operator>>(std::istream& is, const sTeam& team)
{
    return is >> team.blActive >> team.//other data
}

Ja, ich denke (auch) hier sagt ein kurzes Codesnippet mehr als tausend Worte.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
#include <stringstream>
//...
for(std::string s; std::getline(load, s); ) // so lange Zeilen auslesen, bis nichts mehr auszulesen ist
{
    std::stringstream ss(s);
    sTeam team;
    if(!(ss >> team)
    {
        //Die Datei ist beschädigt oder wir waren zu blöd zum auslesen oder beides :)
    }
    vecTeams.push_back(team);
}

[edit]So wäre ein mehr oder weniger genereller Weg, aber natürlich kann man hier auch bspw. mit dem std::istream_iterator arbeiten.
naja.[/edit]
Ach und vergiss deine ifstream-Instanz nicht das std::ios::binary Flag zu geben.
Und das wars. Hoffe konnte wenigstens marginal helfen.

MfG
Check

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Checkmateing« (05.06.2014, 00:32)


David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

3

05.06.2014, 00:17

Man sollte ihm vielleicht erklären, warum man einen Vektor nicht einfach so in eine Datei schreiben kann.
sizeof(vecTeams) wird dir immer denselben Wert liefern, egal wie viele Objekte im Vektor sind. Der Vektor enthält nämlich nur einen Zeiger auf die Daten. Diesen Zeiger hast du in eine Datei geschrieben, aber nicht die eigentlichen Daten. Klar, dass das nicht geht. Dass die Dateigröße nicht hinhaut und auch deine Daten gar nicht in der Datei stehen, hätte dich schon skeptisch machen müssen.

JustSid

Frischling

Beiträge: 54

Beruf: Lead Idiot

  • Private Nachricht senden

4

05.06.2014, 00:46

Was auch noch erwähnt werden sollte ist das C++ es dir erlaubt pointer auf vector Elemente zu holen! Und C++11 bietet dir die handliche data() methode an um an den unterliegenden Speicherblock zu kommen. Dabei ist garantiert dass das ein zusammenhängender Block ist! Folgendes geht beides:

Quellcode

1
2
3
4
std::vector<foo> bar // ...

const foo *t1 = &bar[0]; // Vor C++11
const foo *t2 = bar.data() // Seit C++11


Das aus dem Wege, noch ein paar Dinge:
1) Benutze static_cast<>(), const_cast<>() und wenn nötig dynamic_cast<>()! C-Style casts haben in C++ code nichts zu suchen und erlauben es dir Dinge zu tun vor denen dich der Compiler normalerweise beschützen könnte und würde, wenn du ihm nicht die Hände binden würdest. Außerdem erlauben sie dir dem compiler die Intention deines casts mitzuteilen, womit der dann wiederum schlauer optimieren kann
2) Einfaches hin und her kopieren von Daten via zbsp. memcpy() oder Freunde geht in C++ idR nicht! Nur POD oder seit C++11 trivially copyable Types kannst du einfach so rumkopieren. C++11 bietet dir dafür das praktische std::is_trivially_copyable<> type trait. Willst du rumkopieren? Knall ein static_assert() in deinen Code um zu garantieren das du das auch wirklich kannst. Aber, ganz ehrlich, std::copy() und ähnliche C++ Funktionen sind _immer_ den C Funktionen vorzuziehen!
3) Strict aliasing! Du kannst nicht einfach so dahergehen und Daten hin und her konvertieren wie du lustig bist. Das bricht die strict aliasing rules und ist undefined behaviour, sprich der compiler ist frei deine Festplatte zu formatieren oder das Universum auszulöschen. Ich hänge an diesem Universum und würde daher doch stark bitten von undefined behaviour die Finger zu lassen.
4) Google hat fürs serialisieren eine Library namens protocol buffers. Ist unit tested, wird in vielen Projekten genutzt und funktioniert. Brauchst du das Rad nicht neu für erfinden :)
Jabberwock is killing user

5

05.06.2014, 16:47

Dank!

Vielen Dank erstmal für die schnellen Rückmeldungen - damit sollte ich etwas anfangen können! :thumbup:
Habe mir heute noch auf dem Weg zum PC eine Alternative für mein aktuelles Problem überlegt.
Die Hinweise werde ich auf jeden Fall brauchen um dann den Spielstand zu speichern - das Ergebnis poste ich dann mal an anderer Stelle.
Vielen Dank noch mal!!!

Werbeanzeige