String wird falsch aus Datei gelesen (Binär)



  • Und das ganze new/delete Zeugs ist auch sehr hässlich, warum machst du das?



  • @pumuckl: danke genau das wars. mit \0 liesst er wirklich nur noch TestString ein 🙂

    @cooky451: ja ich weiss, nicht nur die new/delete sache, auch die ganzen Typkonvertierungen sind extrem hässlich :(. Aber irgendwie hab ich bis jetzt keine andere "Lösung" gefunden. Ich denke auch dass er mir genau deswegen immer wieder mal ne Zugriffsverletzung wirft. Hast du eine Idee wie man das ein bisschen schöner lösen könnte?


  • Mod

    Rookie86 schrieb:

    @cooky451: ja ich weiss, nicht nur die new/delete sache, auch die ganzen Typkonvertierungen sind extrem hässlich :(. Aber irgendwie hab ich bis jetzt keine andere "Lösung" gefunden. Ich denke auch dass er mir genau deswegen immer wieder mal ne Zugriffsverletzung wirft. Hast du eine Idee wie man das ein bisschen schöner lösen könnte?

    new[] bedeutet immer, dass man eigentlich std::vector (oder etwas vergleichbares) möchte.



  • Rookie86 schrieb:

    Hast du eine Idee wie man das ein bisschen schöner lösen könnte?

    Bei dem Puffer: Rohes Array / std::array / std::vector
    Bei dem BinaryFile:

    class A
    {
      void foo()
      {
      }
    };
    int main()
    {
      A a; // Nichts, kein new
      a.foo();
    } // Alles wird ganz normal aufgeräumt.
    


  • Rookie86 schrieb:

    class BinaryFile
    {
    	std::fstream m_file;
    
    //...
    
    	template<class T>
    	void WriteValue(T value)
    	{
    		m_file.write((const char*)(&value), sizeof(T));
    	}
    

    Das halte ich für gefählich, da der Typ hier automatisch deduziert werden kann. Ich würde den Benutzer zwingen, den richtigen Typ anzugeben:

    template<class T> struct id {typedef T type;};
    
    :::
    
        template<class T>
        void WriteValue(typename id<T>::type value)
        ::::
    

    Ein portables Dateiformat kommt dabei aber auch nicht heraus.

    Rookie86 schrieb:

    void WriteValue(std::string value)
    	{
    		WriteValue(value.size());
    		m_file.write((const char*)(value.c_str()), value.size() * sizeof(char));
    	}
    

    Tja, was liefert denn value.size() zurück -- vom TYP her?

    Rookie86 schrieb:

    template<class T>
    	void ReadValue(T& value)
    	{
    		char* temp = new char[sizeof(T)];
    		m_file.read(temp, sizeof(T));
    		value = (T)(*temp);
    		delete temp;
    	}
    

    Das delete passt nicht zum new[]. Es geht auch komplett ohne new[]/delete[]. Lass von new/delete und ganzbesonders new[]/delete[] einfach die Finger.

    Rookie86 schrieb:

    void ReadValue(std::string& value)
    	{
    		unsigned int chars = 0;
    		ReadValue(chars);
    

    Aha. Wer sagt denn, dass "unsigned int" der richtige Typ hier ist, der auch von string::size zurückgegeben wird? Das kann hier nach hinten losgehen...

    Rookie86 schrieb:

    char* temp = new char[chars];
    		m_file.read(temp, chars*sizeof(char));
    		value = temp;
    		delete temp;
    	};
    

    Schon wieder new[] mit falschen delete. Nimm doch einfach std::vector<char>. Das ist dann auch Ausnahme-sicher. Also, wenn dir zwischendurch eine Ausnahme um die Ohren fliegt, dann gibt std::vector<char> ordnungsgemäß den Speicher frei, wobei bei Dir ein Speicherleck entstehen würde. Es hat schon seinen Sinn, dass wir Resourcen (inklusive Speicher) "an benutzerdefinierte Objekte binden". Rohe Zeiger sind einfach dämlich. Die fühlen sich nicht für die Verwaltung des Speichers, auf den sie zeigen, verantwortlich. Von dawegen hast Du hier mit Ihnen die manuelle Speicherverwaltung an der Backe...



  • Wenn wir schon dabei sind

    m_file.write((const char*)(value.c_str()), value.size() * sizeof(char));
    

    Wozu dient der cast zu const char*? c_str() liefert das doch schon.



  • Habs jetzt umgeschrieben und bin endlich das new/delete-Zeugs losgeworden:

    template<class T>
    
    	void WriteValue(T value)
        {
            m_file.write(reinterpret_cast<const char*>(&value), sizeof(value));
        }
    
        void WriteValue(std::string value)
        {
    		value.push_back('\0');
    		WriteValue<size_t>(value.size());
            m_file.write(value.c_str(), value.size() * sizeof(char));
    	}
    
        template<class T>
        void ReadValue(T value)
        {
      		m_file.read(reinterpret_cast<char*>(&value), sizeof(value));
        }
    
        void ReadValue(std::string value)
        {
            size_t chars = 0;
            ReadValue<size_t>(chars);
    		m_file.read(reinterpret_cast<char*>(&value), chars*sizeof(char));
        }
    
    #include <iostream>
    
    #include "BinaryFile.h"
    
    int main(int argc, char** argv)
    {
    	std::string tmpString = std::string("TestString");
    	int i1 = 1;
    	int i2 = 23;
    
    	BinaryFile* bf = new BinaryFile("test.txt");
    	bf->WriteValue<std::string>(tmpString);
    	bf->WriteValue<int>(i1);
    	bf->WriteValue<int>(i2);
    	delete bf;	
    
    	BinaryFile* bf2 = new BinaryFile("test.txt");
    	bf->ReadValue<std::string>(tmpString);
    	bf->ReadValue<int>(i1);
    	bf->ReadValue<int>(i2);
    	delete bf2;	
    
    	std::cout << tmpString.c_str() << std::endl;
    	std::cout << i1 << std::endl;
    	std::cout << i2 << std::endl;
    	return 0;
    };
    

    Wenn er durch läuft gibt er mir die richtigen Werte aus... so weit so gut.
    Die Zugriffsverletzungen treten aber immer noch auf :(.

    @krümelkacker:

    value.size() ist vom Typ size_t. size_t ist aber nichts anderes als typedef unsigned int size_t. Trotzdem habe ich den Typ einmal auf size_t gewechselt.

    template<class T> struct id {typedef T type;};
    
    :::
    
        template<class T>
        void WriteValue(typename id<T>::type value)
        ::::
    

    Wie das funktioniert verstehe ich noch nicht ganz (deshalb ist es noch nicht im Code), habe aber bei jedem Write/Read den Typ manuell angegeben als würde mich der Code schon dazu zwingen.

    @Braunstein: Ja logisch... hab die unnötige Konvertierung entfernt.


  • Mod

    void WriteValue(T value)
        {
            m_file.write(reinterpret_cast<const char*>(&value), sizeof(value));
        }
    

    😮 Ich übersetz das mal: Du schreibst an die Stelle deiner Variable value, welche eine lokale Variable in deiner Funktion ist, soviele chars wie der Wert dieser Variablen.

    Wenn's schon mit so einem Hauer losgeht, lese ich lieber gar nicht erst weiter.



  • @SeppJ: Bis auf die Tatsache, dass er den value per const-ref nehmen könnte, sehe ich da nichts schlimmes. Bin ich blind?


  • Mod

    314159265358979 schrieb:

    @SeppJ: Bis auf die Tatsache, dass er den value per const-ref nehmen könnte, sehe ich da nichts schlimmes. Bin ich blind?

    Kleiner Übersetzungsfehler: Er schreibt nicht so viele Bytes wie im Wert stehen, sondern er überschreibt den Wert einfach. Das zerschießt ihm dann zwar nicht den Stack, es macht einfach nur gar nichts. Und wenn die Funktion dann noch so aufgerufen wird:

    WriteValue<size_t>(value.size());
    

    Dann ist klar, dass da was total nicht stimmt.



  • @SeppJ:

    Ich übersetz das mal: Du schreibst an die Stelle deiner Variable value, welche eine lokale Variable in deiner Funktion ist, soviele chars wie der Wert dieser Variablen.

    Nicht ganz, er schreibt von der Position von value soviele Bytes wie value gross ist, bspw. bei einem char also 1 Byte in die Datei. Was ja nicht falsch ist 😉

    Sorry, aber im Moment check ichs einfach überhaupt nicht mehr... ist wohl ne kurze Pause nötig und dann muss ich den Code noch mal genau durchspielen bzw. debuggen.



  • void ReadValue(std::string value)
        {
            size_t chars = 0;
            ReadValue<size_t>(chars);
            m_file.read(reinterpret_cast<char*>(&value), chars*sizeof(char));
        }
    

    Das hier geht gar nicht. Du kannst doch nicht einfach auf die Position deines Strings irgendwelche Daten schreiben. std::string ist doch kein Array.
    Wie wärs denn damit.

    std::string ReadValue()
        {
            size_t size = 0; // name size gefällt mir hier einfach besser
            ReadValue(size);
            std::vector<char> tmp(size);
            m_file.read(&tmp[0], size * sizeof(char)); // das sizeof(char) finde ich hier unnötig
            return std::string(tmp.begin(), tmp.end());
        }
    

    PS Sepps Kritik kann ich hier nicht nachvollziehen. write schreibt schließlich in den Stream und nicht in die Variable.


  • Mod

    Rookie86 schrieb:

    Nicht ganz, er schreibt von der Position von value soviele Bytes wie value gross ist, bspw. bei einem char also 1 Byte in die Datei. Was ja nicht falsch ist 😉

    Oh, ähh. Ich brauch 'nen Kaffee. Aber schnell...



  • @Braunstein: Es funktioniert! 😃

    Boah, war das ein Kampf... wieder was gelernt.

    Vielen dank für eure Hilfe 👍


Anmelden zum Antworten