C++ 数据序列化做什么?
我很难理解序列化是什么,做什么 让我简化我的问题。我的c/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);
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
位,这意味着它们自然具有二进制表示,所以我们不能直接通过套接字发送这些文件吗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' );
}
}