Wie man std::string-Werte aus/in Binärdateien liest/schreibt

Lesezeit: 4 Minuten

Benutzer-Avatar
Chan

Ich möchte ein Item in eine Binärdatei schreiben, es schließen und wieder öffnen, um es zu lesen. Der Code ist einfach und unkompliziert, er wurde mit Visual Studio 2008 kompiliert und fehlerfrei ausgeführt.

Beim Ausführen mit dem GCC-Compiler wurde jedoch ein “Segmentfehler” angezeigt.

Was mache ich falsch?

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

class Item
{
private:
    string itemID;
    string itemName;
    string itemState;

public:
    Item( const string& id = "i0000", const string& name = "Zero item", const string& state = "not init" )
        : itemID( id ) , itemName( name ) , itemState( state )
    {

    }

    string& operator []( int x )
    {
        if ( 0 == x )
            return itemID;
        if ( 1 == x )
            return itemName;
        if ( 2 == x )
            return itemState;

        return ( string& )"";
    }

    const string& operator []( int x ) const
    {
        if ( 0 == x )
            return itemID;
        if ( 1 == x )
            return itemName;
        if ( 2 == x )
            return itemState;

        return ( string& )"";
    }

public:
    friend istream& operator >>( istream& i, Item& rhs )
    {
        cout << " * ItemID: ";
        getline( i, rhs.itemID );
        cout << " - Item Name: ";
        getline( i, rhs.itemName );
        cout << " - Item State: ";
        getline( i, rhs.itemState );
        return i;
    }

    friend ostream& operator <<( ostream& o, const Item& rhs )
    {
        return o << "ID = " << rhs.itemID
                 << "\nName = " << rhs.itemName
                 << "\nState = " << rhs.itemState << endl;
    }
};

void write_to_file( const string& fn, const Item& item )
{
    fstream outf( fn.c_str(), ios::binary | ios::out );
    Item temp( item );
    outf.write( reinterpret_cast<char *>( &temp ), sizeof( Item ) );
    outf.close();
}

void read_from_file( const string& fn, Item& item )
{
    fstream inf( fn.c_str(), ios::binary | ios::in );

    if( !inf )
    {
        cout << "What's wrong?";
    }
    Item temp;
    inf.read( reinterpret_cast<char *>( &temp ), sizeof( Item ) );
    item = temp;
    inf.close();
}

int main()
{
    string fn = "a.out";
    //Item it( "12", "Ipad", "Good" );
    //write_to_file( fn, it );


    Item temp;
    read_from_file( fn, temp );
    cout << temp;

    return 0;
}

  • Dies hat nichts mit Ihrem Problem zu tun, sondern mit den beiden Zeilen return ( string& )"" in den beiden operator[] Funktionen sind undefiniertes Verhalten. Sie konstruieren (implizit) ein Provisorium std::string Objekt in der return-Anweisung und dann einen Verweis auf dieses Temporär zurückgeben, was ein großes No-Go ist. Sie sollten stattdessen einen Verweis auf ein statisches/globales Objekt zurückgeben oder noch besser eine Assertion auslösen oder eine Ausnahme auslösen.

    – Adam Rosenfield

    23. November 2010 um 6:33 Uhr

  • Vielen Dank Adam. Ich verstehe mein Problem jetzt.

    – Chan

    23. November 2010 um 6:54 Uhr

Benutzer-Avatar
Andre Caron

Die zwei Linien:

outf.write( reinterpret_cast<char *>( &temp ), sizeof( Item ) );

und

inf.read( reinterpret_cast<char *>( &temp ), sizeof( Item ) );

sind falsch. Sie schreiben das binäre Layout des Objekts, einschließlich std::string Instanzen. Das heißt, Sie schreiben den Wert von Zeigern in eine Datei und lesen sie zurück.

Sie können Zeiger nicht einfach aus einer Datei lesen und davon ausgehen, dass sie auf einen gültigen Speicher verweisen, insbesondere wenn er von einem temporären Speicher gehalten wurde std::string -Instanz, die den Speicher in ihrem Destruktor hätte freigeben sollen, als sie den Gültigkeitsbereich verließ. Ich bin überrascht, dass Sie dies mit jedem Compiler “richtig” ausgeführt haben.

Ihr Programm sollte den Inhalt schreiben und ihn mit Ihrem zurücklesen operator<< und operator>> Methoden. Es sollte wie folgt aussehen:

void write_to_file( const string& fn, const Item& item )
{
    fstream outf( fn.c_str(), ios::binary | ios::out );
    outf << item << std::endl;
    outf.close();
}

void read_from_file( const string& fn, Item& item )
{
    fstream inf( fn.c_str(), ios::binary | ios::in );
    if( !inf )
    {
        cout << "What's wrong?";
    }
    inf >> item;
    inf.close();
}

BONUS: Es gibt ein paar Macken mit Ihrem Code.

Diese Anweisung wird glücklicherweise derzeit in Ihrem Programm nicht verwendet (die Methode wird nicht aufgerufen).

return ( string& )"";

Es ist ungültig, da Sie einen Verweis auf ein temporäres Zeichenfolgenobjekt zurückgeben. Denken Sie daran, dass ein String-Literal "" ist kein std::string Objekt und Sie können keinen Verweis darauf vom Typ erhalten std::string&. Sie sollten wahrscheinlich eine Ausnahme auslösen, aber Sie könnten damit davonkommen:

string& operator []( int x )
{
    static string unknown;
    if ( 0 == x )
        return itemID;
    if ( 1 == x )
        return itemName;
    if ( 2 == x )
        return itemState;
    return unkonwn;
}

Dies ist eine schlechte Lösung, da die Zeichenfolge als Referenz zurückgegeben wird. Es kann vom Aufrufer geändert werden, sodass möglicherweise nicht immer die zurückgegeben wird "" Wert, den Sie dachten, es würde. Es wird jedoch undefiniertes Verhalten für Ihr Programm entfernen.

Das .close() Methodenaufrufe an std::fstream Objekte sind nicht erforderlich, der Destruktor ruft es automatisch auf, wenn das Objekt den Gültigkeitsbereich verlässt. Das Einfügen des Anrufs dort ist zusätzliche Unordnung.

Und was ist mit der redundanten Namenskonvention?

class Item
{
private:
    string itemID;
    string itemName;
    string itemState;
// ...
};

Was ist falsch daran, sie anzurufen? ID, name und state?

  • Das ist so eine tolle Antwort! Vielen Dank Andre, ich habe es sehr geschätzt.

    – Chan

    23. November 2010 um 6:53 Uhr

1019200cookie-checkWie man std::string-Werte aus/in Binärdateien liest/schreibt

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy