How to read / write std :: string values ​​from / to binary files

I want to write an element to a binary file, close it and open it again to read it. The code is simple and straightforward, it is compiled and works without errors using Visual Studio 2008.

However, when working with the GCC compiler, it received a “segment fault”.

What am I doing wrong?

#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 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; } 
+4
source share
1 answer

Two lines:

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

and

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

wrong. You write a binary object layout, including instances of std::string . This means that you write the value of pointers to a file and read them back.

You cannot just read pointers from a file and assume that they point to real memory, especially if it was stored by a temporary instance of std::string , which was supposed to free the memory in it with the destructor when it went out of scope. I am surprised that you got this to run “correctly” with any compiler.

Your program should write the content and read it using the operator<< and operator>> methods. It should look like this:

 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 wrong?"; } inf >> item; inf.close(); } 

BONUS : There are a few quirks with your code.

This statement, fortunately, is not currently used in your program (the method is not called).

 return ( string& )""; 

Not valid because you will return a link to a temporary string object. Remember that the string literal "" not a std::string object, and you cannot get a link to it of the std::string& . You should probably throw an exception, but you could avoid:

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

This is a bad decision, given that the string is returned by reference. It can be changed by the caller, so it may not always return the value "" , which, in his opinion, would be. However, it will remove the undefined behavior for your program.

Calling the .close() method for std::fstream objects is not required; the destructor automatically calls it when the object goes out of scope. Inserting a call causes additional clutter.

Also, what's the redundant naming convention?

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

What is wrong with their call to ID , name and state ?

+9
source

All Articles