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

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

11

24.06.2012, 21:28

OK, ich weiß warum.
Dass es funktioniert, war Glück.
Scheinbar ist dein std::string so implementiert, dass besonders kurze Strings direkt im Objekt gespeichert werden.
Sobald sie aber etwas länger werden, wird nur noch ein Zeiger darauf gespeichert, und dann geht deine Methode nicht mehr.
Probier mal aus, was passiert, wenn du deinen String sagen wir mal 100 oder 1000 Zeichen lang machst.

Ich kann dich schon sagen hören: "Egal, es funktioniert ja bei mir und ich brauche nur kurze Strings!"
Darauf antworte ich dir jetzt schonmal: Dass es funktioniert, ist reine Glückssache. Es liegt nur daran, wie diese konkrete Implementierung von std::string aussieht. Bei der nächsten Version von Visual C++ oder bei einem anderen Compiler könnte es nicht mehr funktionieren. Auf sowas sollte man sich nie verlassen. Außerdem wirst du später noch auf ähnliche Probleme stoßen, und dann kommst du nicht mehr so einfach drum herum.

Sylence

Community-Fossil

Beiträge: 1 663

Beruf: Softwareentwickler

  • Private Nachricht senden

12

24.06.2012, 21:28

Wie David schon sagte: Das *kann* nicht funktionieren.
std::string ist im prinzip sowas:

Quellcode

1
2
3
4
5
class string
{
char* data;
int size;
};


Wenn du das also in eine Datei schreibst hast du nur die *Adresse* im Speicher *zum Zeitpunkt* des Speicherns, des *ersten* Buchstabens und eine Zahl, die die Länge des Strings angibt. Und kein bisschen mehr.

13

24.06.2012, 21:33

Okey ich werde es überarbeiten ;)

Hättet ihr nen Ratschlag? :S

----------------------------------
Kann geschlossen werden.
--------------------------------
Mache es im Klartext.
Only God can judge me.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »denniro« (24.06.2012, 22:24)


Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

14

24.06.2012, 22:41

Wenn du weißt wie groß deine String max werden können (musst du auf jeden Fall absichern), dann kannst du auf ein statisches Array zurückgreifen und somit deine Struktur in eine POD Struktur verwandeln, aber der c++ weg wäre eher

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Spielstand speichern
void CMenu::SaveScore(int scoreid, int level)
{
    score[scoreid].Level = level;
    if(level == 0)
        score[scoreid].scoreName = "empty";
    else
        score[scoreid].scoreName = scoreText[scoreid].ToAnsiString();
    std::ofstream openFile;
    char savePath[50];
    sprintf_s(savePath, "Data/Score/Score%i.load", scoreid+1);
    openFile.open(savePath, std::ios::out | std::ios::binary);
    if(openFile.is_open())
        openFile << score[scoreid].Level << score[scoreid].scoreName;
    openFile.close();
}

Oder mit ein paar persönliche Präferenzen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
//Spielstand speichern
void CMenu::SaveScore(int scoreid, int level)
{
    std::ofstream openFile;
    std::stringstream savePath;//include <sstream>
    savePath << "Data/Score/Score" << scoreid+1 << ".load";

    openFile.open(savePath.str().c_str(), std::ios::out | std::ios::binary);
    if(openFile.is_open())
        openFile << level << (!level ? "empty" : scoreText[scoreid].ToAnsiString());
    openFile.close();
}

Alternativ kann man auch den << operator für deine Struktur überladen und somit "openFile << score[scoreid].Level << score[scoreid].scoreName;" durch "openFile << score[scoreid];" ersetzen. Fürs Laden ist es analog.

Ohh hmm. Funktioniert wahrscheinlich nicht mit binary; zumindest die beiden geposteten Varianten (bin irgendwie nicht mehr so drinne in C++). Das mit dem POD bzw überladen des << bzw >> operator gehts dennoch auch wenn man in diesem dann doch wieder write/read nutzt.
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

15

24.06.2012, 22:57

Bin schon daran gescheitert, trotzdem danke für deine Hilfe.
lg. denniro
Only God can judge me.

Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

16

25.06.2012, 00:18

Nana kein Grund das "scheitern" zu nennen. Es geht z.B. so:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
struct serializer
{
    std::fstream            str;

    serializer(const char* filename)
    {
        str.open(replayfilename, std::ios::binary | std::ios::out | std::ios::trunc);
    }

    template<class T> serializer& operator << (const T &in)
    {
        str.write((char*)&in,sizeof(in));
        return *this;
    }

    serializer& operator << (const std::string &in)
    {
        unsigned int size = in.size() + 1;//wir wollen auch die Nullterminierung mitnehmen
        
        str.write((char*)&size,sizeof(size));//notwendig damit wir hinterher wissen wieviel wir wieder lesen müssen. ginge aber auch mit der \0 terminierung, aber dieser ansatz geht auch für nicht strings
        str.write(in.c_str(),size);
        return *this;
    }
};

struct deserializer
{
    std::fstream            str;

    deserializer(const char* filename)
    {
        str.open(replayfilename, std::ios::binary | std::ios::in);
    }

    template<class T> deserializer& operator >> (T &out)
    {
        str.read((char*)&out,sizeof(out));
        return *this;
    }

    deserializer& operator >> (std::string &ret)
    {
        unsigned size;
        str.read((char*)&size,sizeof(size));

        char* tmp = new char[size];
        str.read(tmp,size);
        ret = tmp;
        delete tmp;
        return *this;
    }
};


Nutzung wie folgt:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
//Spielstand speichern
void CMenu::SaveScore(int scoreid, int level)
{
    std::stringstream savePath;//include <sstream>
    savePath << "Data/Score/Score" << scoreid+1 << ".load";

    serializer openFile(savePath.str().c_str());
    openFile << level << std::string(!level ? "empty" : scoreText[scoreid].ToAnsiString());
} 

Alternativ kannst du auch in serializer direkt eine Variante für score einbauen, aber das ist dann meist eher unsauber. Nicht verzweifeln, wenn nicht alles direkt verständlich ist. In diese Klassen gehen ein paar Besonderheiten von C++ ein. Ach und keine Garantie, dass es direkt kompiliert und funktioniert, denn es handelt sich um einen schnell abgeänderten Auszug einer deutlich größeren Klasse. Aber das Grundprinzip sollte hier im Vordergrund stehen.

P.S: natürlich ist es zunächst für deinen kleinen Anwendungsfall totaler Overkill, aber diese Klassen werden dann praktisch wenn man viel speichern/laden muss.
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

17

25.06.2012, 09:36

Vielen Dank für deine Hilfe :D
Muss erstmal durch den Code durchblicken und die eine oder andere Sache nachschauen ;)
lg. denniro
Only God can judge me.

Werbeanzeige