Problem beim Schreiben in Binärdatei



  • las mal das "ios:: in" in open weg...

    am ende solltest du die datei schliessen und das fstream objekt freigeben "delete"



  • Wozu überhaupt das fstream-Objekt dynamisch erzeugen? Das macht hier keinen Sinn.



  • Ein flush bzw eine delete (was zwingend noch fehlt) wirkt Wunder.



  • Ahhh danke, hab das fstream Objekt jetzt am Ende freigegeben und jetzt befinden sich auch die geschriebenen Daten nach dem ausführen des Programms in der Datei.

    Jetzt steh ich aber noch vor einem zweiten kleinen Problem, wie schließe ich die Datei, wenn ich über einen Zeiger auf den Datenstrom zugreife?
    Mit einem normalen fstream Objekt sieht es ja so aus:

    int main () {
      fstream filestr;
      filestr.open ("test.txt");
    
      filestr.close();
      return 0;
    }
    

    aber einfach .close() kann ich ja nicht machen, wenn ich einen Zeiger benutze.

    Quellcode schrieb:

    Wozu?

    nimm doch lieber ifstream und ofstream...
    Da kannste deine Sachen einfach so reinschreiben

    Naja ich find meine jetzige Variante besser, weil ich das Ganze eigentlich in Form von Klassen benutze und das praktische ist, dass ich einfach die Addresse einer kompletten Struktur an die read-Funktion übergeben kann und die Funktion dann einfach eine bestimmte Anzahl von Daten in die einzelnen Variablen der Struktur schreibt, ist z.B. praktisch beim lesen/schreiben von Bitmapfileheader/Bitmapinfoheader.



  • hmm, du könntest aber

    filestr->close();
    

    verwenden ...



  • mhhh eigentlich hab ich das schon vorhin ausprobiert, war wahrscheinlich ein Tippfehler drin, den ich nicht gesehn hab.

    Funktioniert jetzt alles wunderbar, danke nochmal für die Hilfe 🙂



  • Nochmal die Frage. Wozu hier überhaupt ein Zeiger?
    Wenn du Zeiger nutzt, reicht am Schluß auch ein

    delete filestr;
    

    Das close() wird dann vom Destruktor automatisch aufgerufen.



  • Naja dynamisch benutz ich es eigentlich in einem anderen Projekt, bei dem es darum geht Bitmaps zu analysieren, bzw zu verändern. Dabei trat halt das Problem auf, dass das mit dem Schreiben nicht klappte, tja jetzt weiß ich ja wiso 😃
    Und weil ich den Fehler finden wollte, hab ich halt einen extra Code nur mit der Schreibfunktion geschrieben, um halt das Problem auf einem Blick zu haben und hier nicht lauter Code-Schnipsel rein stellen zu müssen.



  • Nebukat schrieb:

    Naja ich find meine jetzige Variante besser, weil ich das Ganze eigentlich in Form von Klassen benutze und das praktische ist, dass ich einfach die Addresse einer kompletten Struktur an die read-Funktion übergeben kann und die Funktion dann einfach eine bestimmte Anzahl von Daten in die einzelnen Variablen der Struktur schreibt, ist z.B. praktisch beim lesen/schreiben von Bitmapfileheader/Bitmapinfoheader.

    Vielleicht verstehe ich dich falsch, aber wo wiederspricht das der Verwendung der Streams? Der übliche Weg wäre das man jeweils einen Operator für Eingabe- und Ausgabestreams für die verschiedenen Datentypen anlegt.

    cu André



  • Nebukat schrieb:

    Naja dynamisch benutz ich es eigentlich in einem anderen Projekt,

    Die Frage steht immer noch. Warum?



  • Also wenn ich mal drüber nachdenke gibt es eigentlich keinen zwingenden Grund das fstream-Objekt dynamisch zu erzeugen. Allerdings bin ich gerade dabei mich nach eineinhalbjähriger C++ Abstinenz wieder reinzuarbeiten und beschäftige mich auch zum ersten Mal mit dem Umgang von Dateien.

    Ich habs jetzt umgeschrieben, den Zeigerzugriff und die dynamische Erzeugung des fstream-Objekts hab ich entfernt. Sieht jetzt gekürzt so aus:

    int main()
    {
        BITMAPFILEHEADER BitmapFileheader;
        BITMAPINFOHEADER BitmapInfoheader;
    
        ifstream m_file;
        m_file.open("Unbenannt.bmp", ios:: in | ios:: binary);
        if( !inputStream )
        {
            cerr << "Fehler beim oeffnen" << endl;
            return 1;
        }
    
    //  Lesen der FileHeader Informationen   
        m_file.read(reinterpret_cast<char*>(&BitmapFileheader), sizeof(BITMAPFILEHEADER));
    
    ...
    
        m_file.close();
        system("pause");
        return 0;
    }
    

    Außerdem will ich von diesem reinterpret_cast<char*> weg, nur dabei stoße ich wieder auf ein Problem 🙄
    Mit der read(char* s, streamsize n) Funktion kann man einen Datenblock auslesen, so nun will ich Byte für Byte auslesen, dann muss doch n gleich eins sein und s muss doch dann nur auf einen einfachen char zeigen und wenn der ausgelesene Inhalt dann in einer integer Variablen gespeichert werden soll, brauch ich doch nur schreiben "x = int(*buffer);"; nur irgendwie funktioniert es nicht so, hier noch mal der Code dazu:

    int x = 0;
        char* buffer = new char;
    
        for(int i=0; i<10; i++)
        {
            m_file.read(buffer, 1);
    
            x = int(*buffer);
            cout<<"Inhalt von buffer: "<< x<< endl;
    
        }   
        delete[] buffer;
    


  • Wenn du nur ein char lesen willst, nimm lieber get.

    int x = 0;
        char ch;
    
        for(int i=0; i<10; i++)
        {
            m_file.get(ch);
    
            x = int(ch);
            cout<<"Inhalt von buffer: "<< x<< endl;
    
        }
    

    Das byteweise Einlesen ist aber viel langsamer als das Einlesen ganzer Blöcke mit read oder readsome.



  • Nebukat schrieb:

    Außerdem will ich von diesem reinterpret_cast<char*> weg, nur dabei stoße ich wieder auf ein Problem 🙄

    dann wäre es eine Möglichkeit sich seinen eigenen Binärstream zu basteln. Das geht mittels Ableiten von std::basic_ios<>. Etwa

    class ibinstream : public std::basic_ios< char >
    {
    public:
        typedef std::basic_ios< char > base_type;
        explicit ibinstream( std::streambuf* sb = 0 )
            : base_type( sb )
        {}
    
        ibinstream& operator>>( WORD& x_ )
        {
            if( good() )
            {
                WORD x;
                if( rdbuf()->sgetn( reinterpret_cast< char* >( &x ), sizeof(WORD) ) == sizeof(WORD) )
                    x_ = x;
                else
                    setstate( std::ios_base::failbit | std::ios_base::eofbit );
            }
            return *this;
        }
        ibinstream& operator>>( DWORD& x_ )
        {
            if( good() )
            {
                DWORD x;
                if( rdbuf()->sgetn( reinterpret_cast< char* >( &x ), sizeof(DWORD) ) == sizeof(DWORD) )
                    x_ = x;
                else
                    setstate( std::ios_base::failbit | std::ios_base::eofbit );
            }
            return *this;
        }
    };
    

    Das wäre die Minimalausstattung zum Lesen von WORD und DWORD.

    Auf der Basis baue man sich eine Funktion, die z.B. die BITMAPFILEHEADER-Struktur liest.

    // --   BITMAPFILEHEADER
    ibinstream& operator>>( ibinstream& in, BITMAPFILEHEADER& hdr )
    {
        const WORD bmp_type = 0x4D42;   // 0x4D42 in memory: 0x42 0x4D means "BM"
        if( in >> hdr.bfType )
        {
            if( hdr.bfType != bmp_type )    
                in.setstate( std::ios_base::failbit );
            else
            {
                in >> hdr.bfSize >> hdr.bfReserved1 >> hdr.bfReserved2 >> hdr.bfOffBits;
            }
        }
        return in;
    }
    

    Das Einlesen der einzelnen Member statt der gesamten Struktur garantiert, dass es keine Probleme beim packing gibt.

    Die Anwendung ist dann relativ straight forward:

    int main()
    {
        using namespace std;
        ifstream file( "Unbenannt.bmp", ios_base::binary );
        if( !file.is_open() )
        {
            cerr << "Fehler beim oeffnen" << endl;
            return 1;
        }
        ibinstream in( file.rdbuf() );  // Binärstream verwenden
        BITMAPFILEHEADER hdr;
        if( in >> hdr )
        {
            cout << "Header gelesen" << endl;
        }
        return 0;
    }
    

    Gruß
    Werner

    PS.: benötigte includes

    #include <iostream>
    #include <fstream>
    #include <windows.h>    // WORD, DWORD, BITMAPFILEHEADER
    


  • Moin,

    erstmal danke für die Antworten, die Idee mit der Erstellung eines eigenen Binärstreams ist ziemlich interessant und ich werd mir in den nächsten Tagen mal das näher zur Gemühte führen 😉

    Allerdings hab ich zu erst noch zwei andere Fragen. Zum einen zur get()-Funktion, die hab ich jetzt implementiert, aber bei der Ausgabe steht zehn mal eine -1, aber eigentlich sollte da zehn mal eine 255 stehn. Hier mal der zugehörige Code:

    //  lesen eines Bytes mit get-Funktion
        int x = 0;
        char buffer;
    
        for(int i=0; i<10; i++)
        {
            m_file.get(buffer);
    
            x = int(buffer);
            cout<<"Inhalt von buffer: "<< x<< endl;
        }
    

    Ich hab auch noch mal in einer C++ Referenz nachgeschaut und dort stand die Funktion unter anderem so drin:

    istream& get ( char& c ); - Extracts a character from the stream and stores it in c.

    und eigentlich nutz ich diese ja auch so und ich verstehe nicht warum das Programm nur mist ausgibt.
    Andererseits funktioniert das Auslesen wiederum, wenn ich die Funktion als "int get();" nutze. Nur würde ich gern verstehen/wissen wo mein Fehler in der obigen Anwendung liegt, weil sonst mach ich ihn vielleicht an einer anderen Stelle wieder.

    Zum anderen hab ich immer noch ein Problem mit der read-Funktion. Ich will nämlich drei Bytes auslesen, ich weiß nur noch nicht ob ich diese mit der get-Funktion einzeln oder in einem Block per read auslesen werde, denke aber eher als Block, weil es schneller geht.
    Zum Problem, es passiert bei der Ausgabe genau das Gleiche wie bei der get-Funktion, es wird statt 255 -1 ausgegeben. Außerdem weiß ich nicht wie ich die drei ausgelesenen char Werte auf einmal oder auch einzeln umwandle und in einen int array speichere. Hier mal der Code, bei dem von den drei ausgelesenen Bytes nur der letzte in einer integer Variablen abgelegt wird:

    //  lesen eines Blockes von 3 Bytes mit read-Funktion
        int x;
        char* buffer = new char[3];
    
        for(int i=0; i<1; i++)
        {
            m_file.read(buffer, 3);  //es werden 3 Werte ausgelesen
            x = int(*buffer);  //wie speichere ich nur einen bestimmten ausgelesenen wert?
            cout<<"Inhalt von buffer: "<< x<< endl;
        }  
        delete[] buffer;
    

    Oh man hätte nicht gedacht, dass alleine der Ausleseteil so verzwickt wird. Ich muss mich an dieser Stelle schon mal im Voraus für eure Hilfe und beim Lesen dieses langen Beitrags bedanken.



  • Nebukat schrieb:

    Allerdings hab ich zu erst noch zwei andere Fragen. Zum einen zur get()-Funktion, die hab ich jetzt implementiert, aber bei der Ausgabe steht zehn mal eine -1, aber eigentlich sollte da zehn mal eine 255 stehn. Hier mal der zugehörige Code:

    //  lesen eines Bytes mit get-Funktion
        int x = 0;
        char buffer;
        
        for(int i=0; i<10; i++)
        {
            m_file.get(buffer);
    
            x = int(buffer);
            cout<<"Inhalt von buffer: "<< x<< endl;
        }
    

    Das ist das Problem mit dem signed unsigned mix. Mache einfach

    x = int(static_cast< unsigned char >( buffer ));
    

    Dein zweites Problem habe ich nicht verstanden. Das Problem ist doch mit dem obigen cast zu lösen - oder ?

    Gruß
    Werner



  • Was eine falsche Umwandlung alles für Fehler erzeugen kann, unglaublich. Jetzt werden die richtigen Werte ausgegeben.

    Dein zweites Problem habe ich nicht verstanden. Das Problem ist doch mit dem obigen cast zu lösen - oder ?

    Ja und nein, weil mein zweites Problem bestand aus zwei einzelnen Problemen. Das Problem der falschen Werte ist dank der nun richtigen Typumwandlung behoben. Das zweite Problem war, dass ich nicht wusste wie ich die mit der read-Funktion ausgelesenen drei Werte jeweils in eine Variable speichere, aber mir kam dann doch noch eine Idee und jetzt funktioniert das Auslesen per read-Funktion auch bestens. Die Funktion sieht jetzt so aus:

    //  lesen eines Blockes von 3 Bytes per read-Funktion
        int x;
        char* buffer = new char[3];
    
        for(int i=0; i<6; i++)
        {
            m_file.read(buffer, 3);  //es werden 3 Werte ausgelesen
    
            cout<<"Die drei Farbanteile fuer das "<<i+1<<". Pixel:"<<endl;
            x = int(static_cast< unsigned char >( *buffer ));   // erstes Byte umwandeln
            cout<<"Blau: "<< x<< endl;
            ++buffer;      //An die zweite Stelle des char array gehn
    
            x = int(static_cast< unsigned char >( *buffer ));   // zweites Byte umwandeln
            cout<<"Guen: "<< x<< endl;
            ++buffer;     //An die dritte Stelle des char array gehn
    
            x = int(static_cast< unsigned char >( *buffer ));   // drittes Byte umwandeln
            cout<<"Rot: "<< x<< endl<< endl;
        }  
        delete[] buffer;
    

    Noch mal danke an alle die mir geholfen haben 🙂



  • oder so

    for(int i=0; i<6; i++)
        {
            m_file.read(buffer, 3);  //es werden 3 Werte ausgelesen
    
            cout<<"Die drei Farbanteile fuer das "<<i+1<<". Pixel:"<<endl;
            x = int(static_cast< unsigned char >( buffer[0] ));   // erstes Byte umwandeln
            cout<<"Blau: "<< x<< endl;
    
            x = int(static_cast< unsigned char >( buffer[1] ));   // zweites Byte umwandeln
            cout<<"Guen: "<< x<< endl;
    
            x = int(static_cast< unsigned char >( buffer[2] ));   // drittes Byte umwandeln
            cout<<"Rot: "<< x<< endl<< endl;
        }
    


  • Moin,

    beim erweitern des Programms haben sich mal wieder zwei neue Fragen ergeben und zwar weiß ich nicht wie ich bei der write-Funktion alle Elemente eines array/vector auf einmal übergeben kann, gibt es dafür vielleicht spezielle Befehle 😕

    //  Bild erstellen
        char pixel_zeile [600];
        srand((unsigned)time(NULL));
    
        for(int j=0;j<200;j++)
        {
            for(int i=0;i<600;i++)
            {
                pixel_zeile [i] = char(static_cast< unsigned char >( rand()%256 ));
            }
            crypt_file.write(  , 600);  //Wie übergibt man alle Elemente eines arrays auf einmal???
        }
    
    //  Bild erstellen
        vector<char> pixel_zeile;
        srand((unsigned) time(NULL));
    
        for(int j=0;j<200;j++)
        {
            for(int i=0;i<600;i++)
            {
                pixel_zeile.push_back(char(static_cast< unsigned char >( rand()%256 )));
            }
             crypt_file.write(  , 600);  //Wie übergibt man alle Elemente eines vectors auf einmal???
             crypt_file.clear();
        }
    


  • Ah mir is doch noch eine Möglichkeit eingefallen.

    char* buffer = new char[600];
        srand((unsigned)time(NULL));
    
        for(int j=0;j<200;j++)
        {
            for(int i=0;i<600;i++)
            {
                buffer [i] = char(static_cast< unsigned char >( rand()%256 ));
            }
            crypt_file.write(buffer, 600);
        }
        delete[] buffer;
    

    Würde mich aber über weiter Vorschläge freuen.


Anmelden zum Antworten