2010-11-23 10 views
4

Ich möchte ein Objekt in eine Binärdatei schreiben, es schließen und es erneut öffnen, um es zu lesen. Der Code ist einfach und unkompliziert, er kompiliert und lief ohne Fehler mit Visual Studio 2008.Wie lese/schreibe ich std :: string Werte von/in Binärdateien

Es hat jedoch "Segmentfehler" beim Ausführen mit dem GCC-Compiler.

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; 
} 
+1

Dies hat nichts mit Ihrem Problem zu tun, aber die beiden Zeilen 'return (string &)" "' in den beiden 'operator []' Funktionen sind ** undefiniertes Verhalten **.Sie konstruieren (implizit) ein temporäres 'std :: string'-Objekt in der return-Anweisung und geben dann einen Verweis auf dieses temporary zurück, was ein großes no-no ist. Sie sollten stattdessen einen Verweis auf ein statisches/globales Objekt zurückgeben oder besser noch eine Assertion auslösen oder eine Ausnahme auslösen. –

+0

Vielen Dank Adam. Ich verstehe mein Problem jetzt. – Chan

Antwort

9

Die beiden Linien:

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

und

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

falsch sind. 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 nicht einfach Zeiger aus einer Datei lesen und davon ausgehen, dass sie auf gültigen Speicher zeigen, besonders wenn sie von einer temporären std::string Instanz gehalten wurde, die den Speicher in seinem Destruktor hätte freigeben müssen, wenn er den Gültigkeitsbereich überschritten hat. Ich bin überrascht, dass Sie das "richtig" mit jedem Compiler laufen lassen.

Ihr Programm sollte den Inhalt schreiben und mit Ihren Methoden operator<< und operator>> zurücklesen. 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 ist glücklicherweise derzeit nicht in Ihrem Programm (die Methode wird nicht aufgerufen).

Es ist ungültig, weil Sie einen Verweis auf ein temporäres Zeichenfolgenobjekt zurückgeben werden. Beachten Sie, dass ein String-Literal "" kein std::string Objekt ist und Sie keine Referenz darauf vom Typ std::string& erhalten können. Sie sollten wahrscheinlich eine Ausnahme auslösen, aber man konnte mit wegkommen: Diese

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

ist eine schlechte Lösung gegeben, dass die Zeichenfolge durch Verweis zurückgegeben wird. Es kann vom Aufrufer geändert werden, so dass es nicht immer den "" Wert zurückgibt, den Sie dachten, es würde. Es wird jedoch undefiniertes Verhalten für Ihr Programm entfernen.

Die Methodenaufrufe .close() auf std::fstream Objekte sind nicht erforderlich, der Destruktor ruft es automatisch auf, wenn das Objekt den Gültigkeitsbereich verlässt. Wenn Sie den Anruf einfügen, entsteht zusätzliches Durcheinander.

Auch was ist mit der redundanten Namenskonvention?

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

Was sie mit Aufruf falsch ID, name und state?

+0

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

+0

Danke für die Erinnerung, ich wusste es wirklich nicht. – Chan

Verwandte Themen