std::ofstream - Kann mann mittels "<<" auch binär schreiben?



  • Hallo,

    folgender Code schreibt die Zahl 64 als Text in die Binärdatei.

    ofstream ofs( "c:\\temp\\tmp\\test.bin", ios::binary );
    unsigned long x = 64;
    ofs << x;
    

    Kann man dem Operator auch eine binäre Ausgabe nahelegen (z.B. Formater o.ä.)?

    Ansonsten nehm ich halt ofs.write( ... ), aber die STL kann doch sonst auch immer alles (nur manchmal etwas kryptisch).

    Danke!



  • Soweit ich weiß muss man write nehmen, ist doch eigentlich auch kein Problem oder??



  • Nö, aber das sieht dann nicht mehr so hübsch aus: 😃

    unsigned long x = 64;
    ofs.write( (char *)&x, sizeof(x) );
    

    Die STL ist doch sonst immer so elegant und so. Da habe ich manchmal Sachen gefunden, von denen ich gar nicht wußte, daß sie gehen...



  • vielleicht so

    template<class T>
    class my_binary
    {
       public:
          binary(T val) : value(val) { }
    
          T value;      
    };
    
    template<class T>
    my_binary<T> binary(const T &val)
    {
       return my_binary<T>(val);
    }
    
    template<class T>
    std::ostream& operator<<(std::ostream &out, const my_binary<T> &bin)
    {
       our.write(reinterpret_cast<const char*>(&bin.value), sizeof(bin.value));
       return out;
    }
    
    int x = 6;
    std::cout << binary(x) << "blub";
    

    habs net getestet. koennte so funktionieren

    meep meep



  • Danke! Das ist ja ein interessantes Beispiel für Templates.
    Ich habe es mal ausprobiert:

    template < typename T > 
    class my_binary 
    { 
    public: 
    	my_binary(T val) : value(val) { } 
    	T value;       
    }; 
    
    template < typename T > 
    my_binary<T> binary(const T &val) 
    { 
    	return my_binary<T>(val); 
    } 
    
    template < typename T > 
    std::ostream& operator<<(std::ostream &out, const my_binary<T> &bin) 
    { 
    	out.write(reinterpret_cast<const char*>(&bin.value), sizeof(bin.value)); 
    	return out; 
    }
    
    int main()
    {
    	unsigned long x = 64;
    
    	ofstream ofs( "c:\\temp\\tmp\\test.bin", ios::binary );
    
    	ofs.write( (char*)&x, sizeof(x) );	// das geht
    	ofs << binary( x );					// das auch
    
    	ofs.close();
    	return 0;
    }
    

    Der Aufruf von binary() gibt ein Platzhalter-Objekt zurück, den der Operator "<<" versteht und entsprechend das richtige tut.


  • Administrator

    Pass bei sowas aber auf die Byte-Reihenfolge auf:
    http://de.wikipedia.org/wiki/Byte-Reihenfolge

    Grüssli



  • Oh ja, das kenn' ich schon! 😃
    Außerdem vergißt man schnell "ios::binary".



  • Jemand hat so eine Klasse mal relativ vollständig gepostet.
    Kleiner Tipp: wenn du danach suchst, würde ich das über google machen. 😉



  • stimpleton schrieb:

    Hallo,

    folgender Code schreibt die Zahl 64 als Text in die Binärdatei.

    ofstream ofs( "c:\\temp\\tmp\\test.bin", ios::binary );
    unsigned long x = 64;
    ofs << x;
    

    Kann man dem Operator auch eine binäre Ausgabe nahelegen (z.B. Formater o.ä.)?

    unter 'binär' wird meist verstanden, dass der Wert so ausgegeben wird, wie er im Speicher steht. Aber bedenke, so ein 'unsigned long' z.B. ist nict auf allen Systemen gleich, es muss noch nicht mal die gleiche Größe haben! Das Little/Big Endian Problem wurde schon erwähnt.

    Weiter muss man wissen, ein ofstream ist ein ostream und dieser ist für die Ausgabe von Text in Menschen lesbarer Form konzipiert.

    stimpleton schrieb:

    .. aber die STL kann doch sonst auch immer alles (nur manchmal etwas kryptisch).

    Genau - ich würde bei der Basisklasse von ostream ansetzen, das ist basic_ios<char>, die schon mal einige Features, z.B. die Fehlerflags und die Verbindung zum streambuf enthält.

    Bei der Lösung mit 'my_binary' (s.u.) wurde das Fehlerhandling ganz vergessen. (@Edit: da hatte ich mich vertan, das erledigt schon das ostream::write)

    So ein binärer Ausgabestream könnte in einer einfachen Form so aussehen:

    // -- obinstream.h
    #include <ios>
    
    class obinstream : public std::basic_ios< char >
    {
    public:
        typedef std::basic_ios< char > base_type;
        obinstream( std::streambuf* sb )
            : base_type( sb )
        {
            if( rdbuf() == 0 )
                setstate( std::ios_base::badbit );
        }
    
        obinstream& operator<<( unsigned long x )
        {
            if( good() )
            {
                if( rdbuf()->sputn( reinterpret_cast< const char* >( &x ), sizeof(x) ) != sizeof(x) )
                    setstate( std::ios_base::failbit );
            }
            return *this;
        }
        // evt. weitere Typen
        // obinstream& operator<<( T x )
    };
    

    Ein passender File-Stream ist dann auch schnell gestrickt:

    // fbinstream.h
    #include "binstream.h"
    #include <fstream>
    
    class ofbinstream : public obinstream
    {
    public:
        explicit ofbinstream( const char* fname )
            : obinstream( &m_fbuf )
            , m_fbuf()
        {
            m_fbuf.open( fname, std::ios_base::out | std::ios_base::binary );
        }
        bool is_open() const
        {
            return m_fbuf.is_open();
        }
    private:
        std::basic_filebuf< char > m_fbuf;
    };
    

    Die Anwendung ist dann genau wie beim Schreiben von Text:

    #include "binstream.h"
    #include "fbinstream.h"
    #include <iostream>
    
    int main()
    {
        ofbinstream out( "c:/temp/tmp/test.bin" );
        unsigned long x = 64;
        if( out << x )
            std::cout << "Ausgabe war ok" << std::endl;
        return 0;
    }
    

    um das close() braucht man sich nicht kümmern, das geht automatisch im Destruktor.
    Siehe dazu auch hier und hier.

    Gruß
    Werner



  • ...



  • Die STL - unendliche Weiten!


Anmelden zum Antworten