我正在尝试使用ifstream和ofstream序列化普通旧数据结构,但我无法使其工作。然后我尝试将我的问题减少到只有char和int的超基本序列化,甚至那都行不通。很明显,我错过了核心基本层面的东西。
对于基本结构:
struct SerializeTestStruct
{
char mCharVal;
unsigned int mIntVal;
void Serialize(std::ofstream& ofs);
};
使用序列化功能:
void SerializeTestStruct::Serialize(std::ofstream& ofs)
{
bool isError = (false == ofs.good());
if (false == isError)
{
ofs.write((char*)&mCharVal, sizeof(mCharVal));
ofs.write((char*)&mIntVal, sizeof(mIntVal));
}
}
为什么这会因以下短程序而失败?
//ultra basic serialization test.
SerializeTestStruct* testStruct = new SerializeTestStruct();
testStruct->mCharVal = 'y';
testStruct->mIntVal = 9;
//write
std::string testFileName = "test.bin";
std::ofstream fileOut(testFileName.data());
fileOut.open(testFileName.data(), std::ofstream::binary|std::ofstream::out);
fileOut.clear();
testStruct->Serialize(fileOut);
fileOut.flush();
fileOut.close();
delete testStruct;
//read
char * memblock;
std::ifstream fileIn (testFileName.data(), std::ifstream::in|std::ifstream::binary);
if (fileIn.is_open())
{
// get length of file:
fileIn.seekg (0, std::ifstream::end);
int length = fileIn.tellg();
fileIn.seekg (0, std::ifstream::beg);
// allocate memory:
memblock = new char [length];
fileIn.read(memblock, length);
fileIn.close();
// read data as a block:
SerializeTestStruct* testStruct2 = new(memblock) SerializeTestStruct();
delete[] testStruct2;
}
当我浏览代码时,我注意到memblock
顶部有一个“y”,所以它可能正常工作,而这只是最后placement new
的问题?在该展示位置之后,我最终得到SerializeTestStruct
,其值为:0,0。
答案 0 :(得分:1)
bool isError = (false == ofs.good());
if (false == isError)
{
ofs.write((char*)&mCharVal, sizeof(mCharVal));
ofs.write((char*)&mIntVal, sizeof(mIntVal));
}
更改为
if ( ofs.good() )
{
ofs.write((char*)&mCharVal, sizeof(mCharVal));
ofs.write((char*)&mIntVal, sizeof(mIntVal));
}
我愿意:
ostream & operator << ( ostream &os, const SerializeTestStruct &mystruct )
{
if ( ofs.good() )
{
os.write((char*)&mystruct.mCharVal, sizeof(mCharVal));
os.write((char*)&mystruct.mIntVal, sizeof(mIntVal));
}
return os;
}
答案 1 :(得分:1)
问题在于:
SerializeTestStruct* testStruct2 = new(memblock) SerializeTestStruct();
这将在先前分配的内存中构造类型SerializeTestStruct
的值初始化对象。它将用{0}填充memblock
,因为值初始化是零初始化,用于POD类型(more info)。
以下是您的代码的快速修复:
SerializeTestStruct* testStruct2 = new SerializeTestStruct;
fileIn.read( (char*)&testStruct2->mCharVal, sizeof(testStruct2->mCharVal) );
fileIn.read( (char*)&testStruct2->mIntVal, sizeof(testStruct2->mIntVal) );
fileIn.close();
// do some with testStruct2
// ...
delete testStruct2;
答案 2 :(得分:1)
以下是您应该阅读的内容:
#include <fstream>
#include <string>
#include <stdexcept>
struct SerializeTestStruct
{
char mCharVal;
unsigned int mIntVal;
void Serialize(::std::ostream &os);
static SerializeTestStruct Deserialize(::std::istream &is);
};
void SerializeTestStruct::Serialize(std::ostream &os)
{
if (os.good())
{
os.write((char*)&mCharVal, sizeof(mCharVal));
os.write((char*)&mIntVal, sizeof(mIntVal));
}
}
SerializeTestStruct SerializeTestStruct::Deserialize(std::istream &is)
{
SerializeTestStruct retval;
if (is.good())
{
is.read((char*)&retval.mCharVal, sizeof(retval.mCharVal));
is.read((char*)&retval.mIntVal, sizeof(retval.mIntVal));
}
if (is.fail()) {
throw ::std::runtime_error("failed to read full struct");
}
return retval;
}
int main(int argc, const char *argv[])
{
//ultra basic serialization test.
// setup
const ::std::string testFileName = "test.bin";
// write
{
SerializeTestStruct testStruct;
testStruct.mCharVal = 'y';
testStruct.mIntVal = 9;
::std::ofstream fileOut(testFileName.c_str());
fileOut.open(testFileName.c_str(),
std::ofstream::binary|std::ofstream::out);
fileOut.clear();
testStruct.Serialize(fileOut);
}
// read
{
::std::ifstream fileIn (testFileName.c_str(),
std::ifstream::in|std::ifstream::binary);
if (fileIn.is_open())
{
SerializeTestStruct testStruct = \
SerializeTestStruct::Deserialize(fileIn);
::std::cout << "testStruct.mCharVal == '" << testStruct.mCharVal
<< "' && testStruct.mIntVal == " << testStruct.mIntVal
<< '\n';
}
}
return 0;
}
样式问题:
new
创建内容。堆栈分配的对象通常是您想要的,并且比从堆中分配的任意生命周期对象更容易管理。如果您使用new
,请考虑使用某种智能指针类型来帮助您管理生命周期。...误
data
的{{1}}成员函数。::std::string
并且内存块事情真的很糟糕,因为它太复杂了。如果你确实使用过它,那么就不要像你那样使用数组删除。最后,由于后面解释的原因,它无论如何都不会起作用。new
,因为它是一个您不需要的派生类。您应始终在具有所需功能的层次结构中使用最基类,除非您有非特定原因不这样做。 ofstream
可以使用基类Serialize
类的功能,但请改用该类型。Serialize
函数,则需要匹配的ostream
函数。以下是对内存布局问题的进一步说明。在内存中,基于x86_64的Linux机器上的结构如下所示:
serialize
deserialize
部分的内容未定义,但通常为+------------+-----------+
|Byte number | contents |
+============+===========+
| 0 | 0x79 |
| | (aka 'y') |
+------------+-----------+
| 1 | padding |
+------------+-----------+
| 3 | padding |
+------------+-----------+
| 4 | padding |
+------------+-----------+
| 5 | 9 |
+------------+-----------+
| 6 | 0 |
+------------+-----------+
| 7 | 0 |
+------------+-----------+
| 8 | 0 |
+------------+-----------+
。但这并不重要,因为该空间从未使用过,只是存在,因此访问以下padding
位于有效的4字节边界上。
磁盘上的结构大小为5个字节,并且完全没有填充部分。所以这意味着当你把它读入内存时,它根本不会与内存结构正确对齐,访问它可能会引起某种可怕的问题。
第一条规则,如果您需要0
功能,则需要int
功能。第二条规则,除非您确切地知道自己在做什么,否则不要将原始内存转储到文件中。在许多情况下,这种方法可以正常工作,但有些重要的情况下它不起作用。除非你知道哪些功能有效,哪些功能无效,否则它们会在某些测试环境中找到正常工作的代码,但是当你尝试在某些测试环境中使用它时会失败。真实的系统。
我的代码仍会将内存转储到文件中。只要您在完全相同的体系结构和平台上读取结果,并且使用与编写时相同版本的编译器编译的代码,它就应该工作。只要其中一个变量发生变化,所有赌注都会被取消。
答案 3 :(得分:0)
我是唯一发现这完全不透明的人:
bool isError = (false == ofs.good());
if (false == isError) {
// stuff
}
为什么不:
if ( ofs ) {
// stuff
}
答案 4 :(得分:0)
在我看来,您需要允许序列化到缓冲区而不是直接到流。写入缓冲区允许嵌套或继承的类写入内存,然后可以将整个缓冲区写入流。将比特和片段写入流是无效的。
在我停止将二进制数据写入流之前,这是我编写的内容:
struct Serialization_Interface
{
//! Returns size occupied on a stream.
/*! Note: size on the platform may be different.
* This method is used to allocate memory.
*/
virtual size_t size_on_stream(void) const = 0;
//! Stores the fields of the object to the given pointer.
/*! Pointer is incremented by the size on the stream.
*/
virtual void store_to_buffer(unsigned char *& p_buffer) const = 0;
//! Loads the object's fields from the buffer, advancing the pointer.
virtual void load_from_buffer(const unsigned char *& p_buffer) = 0;
};
struct Serialize_Test_Structure
: Serialization_Interface
{
char mCharVal;
int mIntVal;
size_t size_on_stream(void) const
{
return sizeof(mCharVal) + sizeof(mIntVal);
}
void store_to_buffer(unsigned char *& p_buffer) const
{
*p_buffer++ = mCharVal;
((int&)(*p_buffer)) = mIntVal;
p_buffer += sizeof(mIntVal);
return;
}
void load_from_buffer(const unsigned char *& p_buffer)
{
mCharVal = *p_buffer++;
mIntVal = (const int&)(*p_buffer);
p_buffer += sizeof(mIntVal);
return;
}
};
int main(void)
{
struct Serialize_Test_Struct myStruct;
myStruct.mCharVal = 'G';
myStruct.mIntVal = 42;
// Allocate a buffer:
unsigned char * buffer = new unsigned char[](myStruct.size_on_stream());
// Create output file.
std::ofstream outfile("data.bin");
// Does your design support this concept?
unsigned char * p_buffer = buffer;
myStruct.store_to_buffer(p_buffer);
outfile.write((char *) buffer, myStruct.size_on_stream());
outfile.close();
return 0;
}
我停止将二进制数据写入流以支持文本数据,因为文本数据不必担心Endianess或接收平台接受哪种IEEE浮点格式。