C++ 数据序列化做什么?

C++ 数据序列化做什么?,c++,serialization,C++,Serialization,我很难理解序列化是什么,做什么 让我简化我的问题。我的c/c++程序中有一个struct info,我可以将此struct数据存储到文件save.bin中,或者通过套接字将其发送到另一台计算机 struct info { std::string name; int age; }; void write_to_file() { info a = {"Steve", 10}; ofstream ofs("save.bin", ofstream::binary);

我很难理解序列化是什么,做什么

让我简化我的问题。我的c/c++程序中有一个
struct info
,我可以将此
struct
数据存储到文件
save.bin
中,或者通过套接字将其发送到另一台计算机

struct info {
    std::string name;
    int age;
};

void write_to_file()
{
    info a = {"Steve", 10};
    ofstream ofs("save.bin", ofstream::binary);
    ofs.write((char *) &a, sizeof(a));   // am I doing it right?
    ofs.close();
}

void write_to_sock()
{
    // I don't know about socket api, but I assume write **a** to socket is similar to file, isn't it?
}
write_to_file
只需将
struct info
对象
a
保存到磁盘,使该数据持久化,对吗?并将其写入套接字,这几乎是一样的,对吗

在上面的代码中,我不认为我使用了数据序列化,但是数据
a
save.bin
中保持不变,对吗

问题

  • 那么序列化有什么意义呢?我需要它吗?如果是,我应该如何使用它

  • 我一直认为任何类型的文件,
    .txt/.csv/.exe/..
    ,都是内存中的
    01
    位,这意味着它们自然具有二进制表示,所以我们不能直接通过套接字发送这些文件吗

  • 高度赞赏代码示例

    但是数据a在save.bin中是持久的,对吗

    不!您的结构包含一个
    std::string
    。具体实施(通过转换为
    char*
    得到的二进制数据没有标准定义,但是实际的字符串数据总是在类框架、堆分配之外的某个地方,因此您无法如此轻松地保存该数据。通过正确地完成序列化,字符串数据会写入类的其余部分,因此您可以我能从文件中读回它,这就是你需要序列化的原因

    方法:你必须以某种方式对字符串进行编码,最简单的方法是先写它的长度,然后写字符串本身。在读回文件时,先读回长度,然后将字节数读入一个新的字符串对象

    我一直认为任何类型的文件,.txt/.csv/.exe/..都是内存中的01位


    是的,但问题是,并不是普遍定义哪个位代表数据结构的哪一部分。特别是,它们以“相反的方式”存储位。如果您天真地读取了一个以不匹配的体系结构编写的文件,您显然会收到垃圾。

    您的字符串将无法正确保存。如果您有不同的计算机,它们的整数表示形式可能不同,例如,不同的编程语言将不会有相同的字符串表示形式

    但当您有指向成员的指针时,您将保存指针地址而不是指向的成员,这意味着您无法再次从文件中获取该数据。如果您的结构需要更改怎么办?所有使用您的数据的软件都需要更改


    是的,您可以通过套接字发送文件,但您需要某种协议,以确保您知道文件的名称以及到达文件末尾的时间。

    您正在玩一个游戏。在非常困难的模式下。您达到了最后一级。您很高兴。两天不间断的游戏正在获得回报。情节将很快结束。您将找到邪恶主谋的动机,你是如何成为英雄的,并将收集在最后一扇门后等待的备受追捧的史诗神器。你在这里不需要重新启动一次

    在幕后,有一个游戏对象,看起来像这样:

    class GameState
    {
       int level;
    }
    
    typedef std::vector<char> Buffer;
    
    void
    writeUInt( Buffer& dest, unsigned value )
    {
        dest.push_back( (value >> 24) & 0xFF );
        dest.push_back( (value >> 16) & 0xFF );
        dest.push_back( (value >>  8) & 0xFF );
        dest.push_back( (value      ) & 0xFF );
    }
    
    void
    writeInt( Buffer& dest, int value )
    {
        writeUInt( dest, static_cast<unsigned>( value ) );
    }
    
    void
    writeString( Buffer& dest, std::string const& value)
    {
        assert( value.size() <= 0xFFFFFFFF );
        writeInt( dest, value.size() )
        std::copy( value.begin(), value.end(), std::back_inserter( dest ) );
        while ( dest.size() % 4 != 0 ) {
            dest.push_back( '\0' );
        }
    }
    
    级别为
    25

    到目前为止,你真的很喜欢这个游戏,但你不想在最后一个老板杀了你的情况下重新开始。因此,直观地说,你按下了
    Ctrl+S
    。但是等等,你会得到一个错误:

    Sorry, saving is disabled.
    
    什么?所以我得从头开始以防我死?这怎么可能

    Drumroll

    开发人员虽然很聪明(他们设法让您连续两天上钩,对吗?)但没有实现序列化

    当你重新启动游戏时,内存会被清理。这个非常重要的
    游戏状态
    对象,你花了2天时间将
    级别
    成员提升到
    25
    ,会被销毁

    如何修复此问题?当您关闭游戏时,操作系统会回收内存。您可以将其存储在哪里?外部服务器?(套接字)磁盘?(写入文件)

    好吧,为什么不呢

    class GameState
    {
        int level;
        void save(const std::string& fileName)
        { /* write level to file */ }
        void load(const std::string& fileName)
        { /* read game state from file */ }
    };
    
    当您按下
    Ctrl+s
    时,
    GameState
    对象将保存到文件中

    神奇的是,当你加载游戏时,会从该文件中读取
    GameState
    对象。你不再需要花2天的时间回到上一个老板那里。你已经在那里了

    真实答案:

    从技术上讲,编写序列化功能相当困难。我建议您使用第三方。Google protocol buffers提供跨平台甚至跨语言的序列化。还有许多其他的

    1.那么序列化的意义是什么?我需要它吗?如果需要,我应该如何使用它

    如上所述,它在运行之间或进程之间(可能在不同的机器上)存储状态。是否需要取决于是否需要存储状态并在以后重新加载

    2.我一直认为任何类型的文件,.txt/.csv/.exe/..都是内存中的01位,这意味着它们自然具有二进制表示,所以我们不能直接通过套接字发送这些文件吗


    它们是。但您不想在玩新游戏时修改
    .exe

    只在内存中写入二进制图像是一种序列化形式,对于一些琐碎的情况,它可以工作。不过,一般来说,您需要解决一些仅转储内存不考虑的问题:

    1.指针 如果数据包含任何指针,当然你不能只是在以后转储一个负载,因为一旦程序终止并重新启动,指针指向的内存地址就没有意义了。许多对象都有“隐藏”指针…例如,无法读取
    typedef std::vector<char> Buffer;
    
    void
    writeUInt( Buffer& dest, unsigned value )
    {
        dest.push_back( (value >> 24) & 0xFF );
        dest.push_back( (value >> 16) & 0xFF );
        dest.push_back( (value >>  8) & 0xFF );
        dest.push_back( (value      ) & 0xFF );
    }
    
    void
    writeInt( Buffer& dest, int value )
    {
        writeUInt( dest, static_cast<unsigned>( value ) );
    }
    
    void
    writeString( Buffer& dest, std::string const& value)
    {
        assert( value.size() <= 0xFFFFFFFF );
        writeInt( dest, value.size() )
        std::copy( value.begin(), value.end(), std::back_inserter( dest ) );
        while ( dest.size() % 4 != 0 ) {
            dest.push_back( '\0' );
        }
    }