代码之家  ›  专栏  ›  技术社区  ›  roxrook

如何从二进制文件读/写STD::字符串值

c++
  •  4
  • roxrook  · 技术社区  · 14 年前

    我想将一个项目写入一个二进制文件,关闭它,然后再次打开它来读取它。该代码简单明了,使用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 回复  |  直到 14 年前
        1
  •  9
  •   André Caron    14 年前

    这两行:

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

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

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

    您不能简单地从一个文件中读取指针并假定它们指向有效的内存,特别是当它由一个临时的 STD::字符串 实例,该实例应该在其析构函数超出作用域时释放该析构函数中的内存。我很惊讶你能用任何编译器“正确”运行这个程序。

    你的程序应该用你的 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();
    }
    

    奖金 :您的代码有一些奇怪之处。

    谢天谢地,此语句目前在程序中未使用(方法未被调用)。

    return ( string& )"";
    

    它无效,因为您将返回对临时字符串对象的引用。记住字符串文字 "" 不是 STD::字符串 对象,但无法获取对其类型的引用 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;
    // ...
    };
    

    打电话给他们有什么问题 ID , name state ?