如何从/向二进制文件读取/写入std :: string值

时间:2010-11-23 06:17:06

标签: c++

我想将一个Item写入二进制文件,关闭它,然后再次打开它来读取它。代码简单明了,使用Visual Studio 2008编译和运行时没有错误。

然而,当使用GCC编译器运行时,它会出现“段错误”。

我做错了什么?

#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 个答案:

答案 0 :(得分:9)

这两行:

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

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

错了。您正在编写对象的二进制布局,包括std::string个实例。这意味着您正在编写指向文件的指针值并将其读回。

你不能简单地从文件中读取指针并假设它们指向有效的内存,特别是如果它由一个临时的std::string实例保存,当它超出范围时应该释放它的析构函数中的内存。我很惊讶你能用任何编译器“正确”运行它。

您的程序应编写内容并使用operator<<operator>>方法将其读回。它应如下所示:

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 :您的代码存在一些怪癖。

幸运的是,这个陈述目前在您的程序中未使用(该方法未被调用)。

return ( string& )"";

它无效,因为您将返回对临时字符串对象的引用。请记住,字符串文字""不是std::string对象,您无法获得std::string&类型的引用。你应该提出异常,但你可以逃脱:

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

这是一个糟糕的解决方案,因为该字符串是通过引用返回的。它可以被调用者修改,因此它可能并不总是返回您认为会""的值。但是,它将删除程序的未定义行为。

.close()对象的std::fstream方法调用不是必需的,当对象超出范围时,析构函数会自动调用它。插入电话会产生额外的混乱。

此外,冗余命名约定是什么?

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

给他们IDnamestate打电话有什么问题?