我不是c ++专家,但我过去曾多次将事情序列化。不幸的是,这次我正在尝试序列化一个包含std :: string的类,我理解这很像序列化指针。
我可以将类写到文件中并再次读回来。所有int字段都没问题,但是std :: string字段给出了“地址越界”错误,大概是因为它指的是不再存在的数据。
这是否有标准的解决方法?我不想回到char数组,但至少我知道它们在这种情况下工作。如有必要,我可以提供代码,但我希望我已经很好地解释了我的问题。
我通过将类转换为char *并使用fstream将其写入文件来进行序列化。阅读当然恰恰相反。
答案 0 :(得分:11)
我通过将类强制转换为char *并将其写入a来进行序列化 用fstream文件。阅读当然恰恰相反。
不幸的是,这只有在没有涉及指针的情况下才有效。您可能希望为自己的课程void MyClass::serialize(std::ostream)
和void MyClass::deserialize(std::ifstream)
打电话,并拨打电话。对于这种情况,你想要
std::ostream& MyClass::serialize(std::ostream &out) const {
out << height;
out << ',' //number seperator
out << width;
out << ',' //number seperator
out << name.size(); //serialize size of string
out << ',' //number seperator
out << name; //serialize characters of string
return out;
}
std::istream& MyClass::deserialize(std::istream &in) {
if (in) {
int len=0;
char comma;
in >> height;
in >> comma; //read in the seperator
in >> width;
in >> comma; //read in the seperator
in >> len; //deserialize size of string
in >> comma; //read in the seperator
if (in && len) {
std::vector<char> tmp(len);
in.read(tmp.data() , len); //deserialize characters of string
name.assign(tmp.data(), len);
}
}
return in;
}
您可能还想重载流操作符以便于使用。
std::ostream &operator<<(std::ostream& out, const MyClass &obj)
{obj.serialize(out); return out;}
std::istream &operator>>(std::istream& in, MyClass &obj)
{obj.deserialize(in); return in;}
答案 1 :(得分:10)
简单地将对象的二进制内容写入文件不仅是不可移植的,而且正如您所知,它不适用于指针数据。你基本上有两个选择:要么你写一个真正的序列化库,它可以正确地处理std :: strings。使用c_str()将实际字符串输出到文件,或者使用优秀的boost serialization库。如果可能的话,我会推荐后者,然后你可以用这样一个简单的代码序列化:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
class A {
private:
std::string s;
public:
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & s;
}
};
此处,函数serialize
可用于序列化和反序列化数据,具体取决于您调用它的方式。有关详细信息,请参阅文档。
答案 2 :(得分:2)
对于具有可变大小的字符串或其他blob,最简单的序列化方法是在序列化整数时首先序列化大小,然后将内容复制到输出流。
读取时首先读取大小,然后分配字符串,然后通过从流中读取正确的字节数来填充它。
另一种方法是使用分隔符和转义,但需要更多代码,并且在序列化和反序列化方面都较慢(但结果可以保持人类可读)。
答案 3 :(得分:1)
如果您的类包含任何外生数据(char*
),则必须使用更复杂的序列化方法,而不是将类强制转换为string
并将其写入文件。并且你对于你为什么会出现分段错误是正确的。
我会创建一个成员函数,它将接受fstream
并读入来自它的数据以及一个反函数,该函数需要fstream
并将其内容写入以便稍后恢复,像这样:
class MyClass {
pubic:
MyClass() : str() { }
void serialize(ostream& out) {
out << str;
}
void restore(istream& in) {
in >> str;
}
string& data() const { return str; }
private:
string str;
};
MyClass c;
c.serialize(output);
// later
c.restore(input);
如果您需要语法糖,您还可以定义operator<<
和operator>>
以使用istream
和ostream
来序列化和恢复您的课程。
答案 4 :(得分:0)
为什么不只是:
std::ofstream ofs;
...
ofs << my_str;
然后:
std::ifstream ifs;
...
ifs >> my_str;
答案 5 :(得分:0)
/*!
* reads binary data into the string.
* @status : OK.
*/
class UReadBinaryString
{
static std::string read(std::istream &is, uint32_t size)
{
std::string returnStr;
if(size > 0)
{
CWrapPtr<char> buff(new char[size]); // custom smart pointer
is.read(reinterpret_cast<char*>(buff.m_obj), size);
returnStr.assign(buff.m_obj, size);
}
return returnStr;
}
};
class objHeader
{
public:
std::string m_ID;
// serialize
std::ostream &operator << (std::ostream &os)
{
uint32_t size = (m_ID.length());
os.write(reinterpret_cast<char*>(&size), sizeof(uint32_t));
os.write(m_ID.c_str(), size);
return os;
}
// de-serialize
std::istream &operator >> (std::istream &is)
{
uint32_t size;
is.read(reinterpret_cast<char*>(&size), sizeof(uint32_t));
m_ID = UReadBinaryString::read(is, size);
return is;
}
};
答案 6 :(得分:-2)
我很久没有编写C ++代码了,但也许你可以序列化一个char
的数组。
然后,当您打开文件时,string
只会指向数组。
只是一个想法。