Unbehandelte Ausnahme nach Laden einer Datei



  • Hallo nochmal

    Hab mir vor kurzem zum Spaß ein kleines Spiel zusammengebastelt, in dem man sich einen Chrakter erstellen und diesen trainieren kann (läuft über Konsole).

    Heute habe ich dann 2 weitere Funktionen hinzugefügt, einmal Spiel speichern und Spiel laden, die die aktuellen Werte des Charakters speichern bzw. laden.
    Das ganze lässt sich über ein Menü aufrufen, dass ich mittels switch case realisiert habe.

    Aber zurück zum Speichern und Laden. Das ganze funktioniert, soweit ich das bisher getestet habe, aber jetzt kommts: Wenn ich einen Spielstand lade, mir daraufhin das Menü angezeigt wird und ich dieses Menü verlassen will, um zum Hauptmenü zurückzukehren kommt diese Errormeldung:

    Unbehandelte Ausnahme bei 0x5571ad54 (msvcp100d.dll) in Test-Version.exe:
    0xC0000005: Zugriffsverletzung beim Schreiben an Position 0xdd32d857.

    Hin und wieder steht anstatt Schreiben auch Lesen.

    void Game::saveGame()
    {
        //-----nicht mehr aktuell------
    
    	std::cout << "\nWirklich speichern? Alter Spielstand wird dabei überschrieben\n"
    		      << "Bestätigen durch Eingabe von (J)a bzw (N)ein: ";
    	std::string str;
    	std::cin >> str;
    	if (str == "j" || str == "J")
    	{
    		std::ofstream Output("Chrakter.ch", std::ios::binary); 
    		if (Output == NULL)
    		{
    			std::cout << "\nDatei zum Speichern konnte nicht geöffnet werden!\n";
    		}
    		else
    		{
               Output.write((char*) &hero, sizeof(hero));
    		   std::cout << "\nSpielstand erfolgreich gesichert\n";
    		}
    	    Output.close();
    
    	}
    	else if (str == "n" || str == "N")
    	{
    		std::cout << "\nSpeichern abgebrochen\n";
    	}
    	else
    	{
    		std::cout << "\nFalsche Eingabe\n";
    	}
    }
    
    void Game::loadGame()
    {
    	std::cout << "\nWirklich laden? Bisherige Erfolge werden gelöscht\n"
    		      << "Bestätigen durch Eingabe von (J)a bzw (N)ein: ";
    	std::string str;
    	std::cin >> str;
    	if (str == "j" || str == "J")
    	{
    		std::ifstream Input("Charakter.ch", std::ios::binary);
    		if(Input == NULL)
    		{
    			std::cout << "\nDatei konnte nicht geöffnet werden!\n";
    		}
    		else
    		{
    			Input.read((char*) &hero, sizeof(hero));
    		    std::cout << "\nSpielstand erfolgreich geladen\n";
    		}
    	    Input.close();
    	}
    	else if (str == "nein" || str == "Nein" || str =="NEIN")
    	{
    		std::cout << "\nLaden abgebrochen\n";
    	}
    	else
    	{
    		std::cout << "\nFalsche Eingabe\n";
    	}
    }
    
    void Manager::startNewGame()
    {
    	char choice=' ';
    	Game goTo;
    	while(choice != 'z')
    	{
    	   showNewGameTextOnTerminal();
    	   choice = ' ';
    	   std::cin >> choice;
    	   switch(choice)
    	   {
    	     case 'n': goTo.createYourHero(); break;
    	     case 'e': goTo.showInformationOfHero(); break;
    		 case 't': goTo.practiceHero(); break;
    		 case 'c': goTo.giveCPs(); break;
    		 case 's': goTo.sleep(); break;
    		 case 'h': goTo.buyItemsfromMarket(); break;
    	     case 'a': goTo.attackEnemy(); break;
    		 case 'b': goTo.initializeEnemy(); break;
    		 case 'i': goTo.showInformationOfEnemy(); break;
    		 case 'p': goTo.saveGame(); break;
    		 case 'l': goTo.loadGame(); break;
    		 case 'z': break;
    		 default: std::cout << "Nicht implementiert!\n"; break;
    	   }
    	}
    }
    

    Hab das ganze auch im Debuger durchgespielt.. der Fehler tritt immer dann auf wenn die Bedingung while(choice != 'z') geprüft und aus der Schleife gesprungen wird, aber NUR nachdem ich den Spielstand geladen habe.
    Ich lande daraufhin in der Datei xutility bei diesem Codeabschnitt:

    // MEMBER FUNCTIONS FOR _Container_base12
    inline void _Container_base12::_Orphan_all()
    	{	// orphan all iterators
     #if _ITERATOR_DEBUG_LEVEL == 2
    	if (_Myproxy != 0)
    		{	// proxy allocated, drain it
    		_Lockit _Lock(_LOCK_DEBUG);
    
    		for (_Iterator_base12 **_Pnext = &_Myproxy->_Myfirstiter;
    			*_Pnext != 0; *_Pnext = (*_Pnext)->_Mynextiter)
    			(*_Pnext)->_Myproxy = 0;
    		_Myproxy->_Myfirstiter = 0;
    		}
     #endif /* _ITERATOR_DEBUG_LEVEL == 2 */
    	}
    

    Der Fehler soll in dieser Zeile passiert sein

    *_Pnext != 0; *_Pnext = (*_Pnext)->_Mynextiter)
    

    Danke und LG



  • was genau ist _PNext ? (bitte um code)



  • Das kann ich leider nicht sagen.. das ist ein Ausschnitt des Quelltextes von der xutility-Datei. In dieser lande ich, wenn die Fehlermeldung ausgeworfen wird.



  • Du zeigst nicht, wie hero definiert ist, ich vermute aber, dass es kein POD ist. Dann funktioniert das Lesen und Schreiben nicht so einfach.



  • hero ist eine Instanz der Klasse Hero (wär hätts gedacht). Diese sieht so aus:

    class Hero
    {
    private:
           //ziemlich überfüllt, ich weiß.. bin schon am überlegen wie ich das verbessern könnte ;D
    	std::string name_;
    	int level_;
        int hitPoints_;
    	int hpMax_;
        int mana_;
    	int manaMax_;
        int creditPoints_;
    	int exp_;
    	int expMax_;
    	int attackPower_;
    	int defensePower_;
    	float money_;
    	std::vector<Item> bag; 
            //dachte anfang es könnte am vector liegen, hab ihn aber auskommentiert -> fehler besteht immernoch
    public:
       ...
    

    Noch die Klasse Item, falls es wichtig sein sollte:

    class Item
    {
    private:
    	std::string name_;
    	int quantity_;
    	int price_;
    public:
       ...
    


  • Mit std::string und std::vector ist es kein POD, du kannst die Klasse also nicht einfach mit read/write behandeln. Stattdessen musst du die Werte der Klasse einzeln schreiben und lesen.



  • Sorex schrieb:

    ziemlich überfüllt, ich weiß.. bin schon am überlegen wie ich das verbessern könnte 😉

    Fasse ähnliche Eigenschaften zu kleinen Strukturen zusammen. Lohnt sich umso mehr, wenn du nicht nur Daten, sondern auch gleich noch Funktionalität reinkapseln kannst.



  • manni66 schrieb:

    Mit std::string und std::vector ist es kein POD, du kannst die Klasse also nicht einfach mit read/write behandeln. Stattdessen musst du die Werte der Klasse einzeln schreiben und lesen.

    Okay, hast recht.. hab jetzt mal nur versucht den Namen in die Datei zu speichern und dann zu laden -> gleicher Fehler.
    Danach hab ich das selbe mit dem Wert Mana (int) gemacht -> Fehler weg

    Welche Möglichkeit gibt's denn jetzt einen String in die Datei zu speichern?

    Fasse ähnliche Eigenschaften zu kleinen Strukturen zusammen. Lohnt sich umso mehr, wenn du nicht nur Daten, sondern auch gleich noch Funktionalität reinkapseln kannst.

    Struct ist ne gute Idee, werde ich morgen mal anpacken.. aber inwiefern "Funktionalität reinkapseln"? Verstehe ich gerade nicht



  • Sorex schrieb:

    Welche Möglichkeit gibt's denn jetzt einen String in die Datei zu speichern?

    Das einfachste ist wohl der Stream-Operator <<, der sollte sich schon um alles notwendige kümmern.



  • Hab das mal so gemacht, gibt aber noch ein klitzkleines "Problem":

    void Game::loadGame()
    {
    	std::cout << "\nWirklich laden? Bisherige Erfolge werden gelöscht\n"
    		      << "Bestätigen durch Eingabe von Ja bzw Nein: ";
    	char str;
    	std::cin >> str;
    
    	if (str == 'j' || str == 'J')
    	{
    		std::ifstream Input("hero.ch");
    		if(Input == NULL)
    		{
    			std::cout << "\nDatei konnte nicht geöffnet werden!\n";
    		}
    		else
    		{  
    
    		   Input.read((char*) &hero.attackPower_, sizeof(hero.attackPower_));
    		   Input.read((char*) &hero.defensePower_, sizeof(hero.defensePower_));
    		   Input.read((char*) &hero.hitPoints_, sizeof(hero.hitPoints_));
    		   Input.read((char*) &hero.hpMax_, sizeof(hero.hpMax_));
    		   Input.read((char*) &hero.mana_, sizeof(hero.mana_));
    		   Input.read((char*) &hero.manaMax_, sizeof(hero.manaMax_));
    		   Input.read((char*) &hero.level_, sizeof(hero.level_));
    		   Input.read((char*) &hero.exp_, sizeof(hero.exp_));
    		   Input.read((char*) &hero.expMax_, sizeof(hero.expMax_));
    		   Input.read((char*) &hero.money_, sizeof(hero.money_));
    		   Input.read((char*) &hero.creditPoints_, sizeof(hero.creditPoints_));
    		   Input >> hero.name_;
    
    		    std::cout << "\nSpielstand erfolgreich geladen\n";
    		}
    	    Input.close();
    
            if(Input.good())
               std::cout << "JA\n";
            else
               std::cout << "NEIN\n";
    	}
    	else if (str == 'n'|| str == 'N')
    	{
    		std::cout << "\nLaden abgebrochen\n";
    	}
    	else
    	{
    		std::cout << "\nFalsche Eingabe\n";
    	}
    }
    

    Komischerweise musste ich die Zeile Input >> hero.name_ an den Schluss packen, da ich sonst nur wirre Ergebnise bekam.
    So wie's jetzt ist funktioniert es jedenfalls, hab aber dann mal zum Test die if-Anweisung "if(Input.good())" mit eingebaut, bekomme dort als Ergebnis aber ständig "NEIN".
    Wird die Zeile Input >> hero.name_ auskommentiert ist das Ergebnis "JA". Woran liegt das?



  • Erstens ist dein Vergleich

    input == NULL
    

    sinnlos. input ist ein Objekt, kein Zeiger.

    Zweitens bezweifle ich, dass du Binärdaten lesen/schreiben möchtest. Du kannst dafür auch einfach die Stream-Operatoren verwenden, die wurden nämlich schon in weiser Vorraussicht von C++ für alle Grunddatentypen (und std::string) überladen 😉

    Drittens, hier ein Schema, wie ich beim Lesen einer Datei vorgehen würde.

    int main()
    {
        std::ifstream is("blah.blubb");
    
        if(!is.is_open())
        {
            std::cout << "Fehler beim Öffnen der Datei" << std::endl;
            return 1;
        }
    
        is >> daten;
    
        if(!is.eof())
        {
            std::cout << "Fehler beim Lesen der Datei" << std::endl;
            return 1;
        }
    
        // is wird am Ende des Scopes automatisch geschlossen. Nennt sich RAII.
    }
    


  • 314159265358979 schrieb:

    Erstens ist dein Vergleich

    input == NULL
    

    sinnlos. input ist ein Objekt, kein Zeiger.

    Da hab ich einfach mal dem Autor des Buches vertraut, aber gut es jetzt besser zu wissen.

    314159265358979 schrieb:

    Zweitens bezweifle ich, dass du Binärdaten lesen/schreiben möchtest. Du kannst dafür auch einfach die Stream-Operatoren verwenden, die wurden nämlich schon in weiser Vorraussicht von C++ für alle Grunddatentypen (und std::string) überladen 😉

    Den Binärdatentyp habe ich in der aktuellen Version nicht mehr drin 🙂

    314159265358979 schrieb:

    Drittens, hier ein Schema, wie ich beim Lesen einer Datei vorgehen würde.

    int main()
    {
        std::ifstream is("blah.blubb");
    
        if(!is.is_open())
        {
            std::cout << "Fehler beim Öffnen der Datei" << std::endl;
            return 1;
        }
    
        is >> daten;
    
        if(!is.eof())
        {
            std::cout << "Fehler beim Lesen der Datei" << std::endl;
            return 1;
        }
    
        // is wird am Ende des Scopes automatisch geschlossen. Nennt sich RAII.
    }
    

    Bis auf den beiden if-Anweisungen hatte ich vorhin das selbe Schema verfolgt. Die Daten wurden nach dem Laden jedoch alle hintereinander angezeigt und nicht in der, für den Wert vorgesehenen Zeile.

    Edit:
    Habe gerade bemerkt, dass sich dieses "alles in einer Zeile Laden" mit einem "\n" beim Schreiben in die Datei umgehen lässt. Ist aber etwas umständlich, zumal das Ergebnis beim if(Input.good()) immernoch false bleibt (wobei ich mir ehrlich gesagt nichmal sicher bin, ob das auch was zu sagen hat).



  • Sorex schrieb:

    Da hab ich einfach mal dem Autor des Buches vertraut, aber gut es jetzt besser zu wissen.

    Welches Buch ist das? Hat der Autor die Initialen J.W.?

    Sorex schrieb:

    Den Binärdatentyp habe ich in der aktuellen Version nicht mehr drin 🙂

    Das hat damit nichts zu tun. Sobald du .read() aufrufst, liest du hier Binär.
    Mit >> liest man formatiert.



  • Hast du auch die Speicher-Methode mit angepasst? Die beiden müssen natürlich Hand in Hand arbeiten, poste optimalerweise nochmal deine jetzige Fassung beider Methoden.



  • Das ist das Problem, wenn man Unreg ist, man kann nicht editieren.

    Dir ist schon klar, dass du beim Speichern eine Datei mit Namen "Chrakter.ch" und NICHT "Charakter.ch" erstellst?



  • Aktuell sehen sie so aus:

    void Game::saveGame()
    {
    	std::cout << "\nWirklich speichern? Alter Spielstand wird dabei überschrieben\n"
    		      << "Bestätigen durch Eingabe von (J)a bzw (N)ein: ";
    	char str;
    	std::cin >> str;
    	if (str == 'j' || str == 'J')
    	{
    		std::ofstream Output("hero.ch");
    		if (Output == NULL)
    		{
    			std::cout << "\nDatei zum Speichern konnte nicht geöffnet werden!\n";
    		}
    		else
    		{
    
    		   Output.write((char*) &hero.attackPower_, sizeof(hero.attackPower_));
    		   Output.write((char*) &hero.defensePower_, sizeof(hero.defensePower_));
    		   Output.write((char*) &hero.hitPoints_, sizeof(hero.hitPoints_));
    		   Output.write((char*) &hero.hpMax_, sizeof(hero.hpMax_));
    		   Output.write((char*) &hero.mana_, sizeof(hero.mana_));
    		   Output.write((char*) &hero.manaMax_, sizeof(hero.manaMax_));
    		   Output.write((char*) &hero.level_, sizeof(hero.level_));
    		   Output.write((char*) &hero.exp_, sizeof(hero.exp_));
    		   Output.write((char*) &hero.expMax_, sizeof(hero.expMax_));
    		   Output.write((char*) &hero.money_, sizeof(hero.money_));
    		   Output.write((char*) &hero.creditPoints_, sizeof(hero.creditPoints_));
    		   Output << hero.name_;
    
    		   std::cout << "\nSpielstand erfolgreich gesichert\n";
    		}
    	    Output.close();
    	}
    	else if (str == 'n'|| str == 'N')
    	{
    		std::cout << "\nSpeichern abgebrochen\n";
    	}
    	else
    	{
    		std::cout << "\nFalsche Eingabe\n";
    	}
    }
    
    void Game::loadGame()
    {
        std::cout << "\nWirklich laden? Bisherige Erfolge werden gelöscht\n"
                  << "Bestätigen durch Eingabe von Ja bzw Nein: ";
        char str;
        std::cin >> str;
    
        if (str == 'j' || str == 'J')
        {
            std::ifstream Input("hero.ch");
            if(Input == NULL)
            {
                std::cout << "\nDatei konnte nicht geöffnet werden!\n";
            }
            else
            {  
    
               Input.read((char*) &hero.attackPower_, sizeof(hero.attackPower_));
               Input.read((char*) &hero.defensePower_, sizeof(hero.defensePower_));
               Input.read((char*) &hero.hitPoints_, sizeof(hero.hitPoints_));
               Input.read((char*) &hero.hpMax_, sizeof(hero.hpMax_));
               Input.read((char*) &hero.mana_, sizeof(hero.mana_));
               Input.read((char*) &hero.manaMax_, sizeof(hero.manaMax_));
               Input.read((char*) &hero.level_, sizeof(hero.level_));
               Input.read((char*) &hero.exp_, sizeof(hero.exp_));
               Input.read((char*) &hero.expMax_, sizeof(hero.expMax_));
               Input.read((char*) &hero.money_, sizeof(hero.money_));
               Input.read((char*) &hero.creditPoints_, sizeof(hero.creditPoints_));
               Input >> hero.name_;
    
                std::cout << "\nSpielstand erfolgreich geladen\n";
            }
            Input.close();
    
            if(Input.good())
               std::cout << "JA\n";
            else
               std::cout << "NEIN\n";
        }
        else if (str == 'n'|| str == 'N')
        {
            std::cout << "\nLaden abgebrochen\n";
        }
        else
        {
            std::cout << "\nFalsche Eingabe\n";
        }
    }
    

    Welches Buch ist das? Hat der Autor die Initialen J.W.?

    Das Buch nennt sich C++ für Spieleprogrammierer und ist von Heiko Kalista



  • bitmap.button schrieb:

    Das ist das Problem, wenn man Unreg ist, man kann nicht editieren.

    Dir ist schon klar, dass du beim Speichern eine Datei mit Namen "Chrakter.ch" und NICHT "Charakter.ch" erstellst?

    Ja, das war die Version von gestern.. hab sie danach auf hero.ch umgetauft (entstehen weniger Tippfehler xD)



  • Wie schon mehrfach erwähnt wurde: Verzichte lieber auf die read()/write() und verwende für alle Daten die Stream-Operatoren.

    @Pi: Hat der eof-Test bei deinem Code eigentlich einen tieferen Sinn?



  • CStoll schrieb:

    @Pi: Hat der eof-Test bei deinem Code eigentlich einen tieferen Sinn?

    Wenn das Ende erreicht wurde, ist alles glatt gelaufen. Wenn nicht, nicht. Ist doch logisch?



  • Erstens erinnere ich mich düster, daß eof erst nach einem erfolglosen Leseversuch gesetzt wird. Und zweitens: Was machst du, wenn in der Datei mehr steht als dein Lese-Operator erfasst hat?



  • CStoll schrieb:

    Erstens erinnere ich mich düster, daß eof erst nach einem erfolglosen Leseversuch gesetzt wird. Und zweitens: Was machst du, wenn in der Datei mehr steht als dein Lese-Operator erfasst hat?

    Im Normalfall lese ich alles aus. Will man das nicht, muss man anders vorgehen.
    Aber du hast wohl Recht, wenn du meinst, dass die Überprüfung mit .fail() besser ist, da immer funktioniert.


Anmelden zum Antworten