序列化一个包含std :: string的类
我不是一个C ++专家,但是我已经在过去几次序列化了一些东西。 不幸的是,这次我试图序列化一个包含一个std :: string的类,我理解它非常像序列化一个指针。
我可以把这个类写出来,然后再读回来。 所有的int字段都可以,但std :: string字段给出了一个“address out of bounds”的错误,大概是因为它指向的数据已经不存在了。
有没有一个标准的解决方法呢? 我不想回到char数组,但至less我知道他们在这种情况下工作。 如有必要,我可以提供代码,但是我希望能够很好地解释我的问题。
我通过将类转换为char *并将其写入fstream文件来序列化。 当然,阅读正好相反。
我通过将类转换为char *并将其写入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; }
您可能还想要重载stream操作符以便于使用。
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;}
简单地将一个对象的二进制内容写入一个文件不仅是不可移植的,而且正如你所认识到的,它不适用于指针数据。 你基本上有两个select:要么写一个真正的序列化库,它通过例如使用c_str()将实际的string输出到文件来正确处理std :: strings,或者使用优秀的boost序列化库。 如果可能的话,我会推荐后者,然后你可以用这样一个简单的代码序列化:
#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
函数用于序列化和反序列化数据,这取决于你如何调用它。 有关更多信息,请参阅文档。
对于string或其他大小可变的最简单的序列化方法是在序列化整数时首先序列化大小,然后将内容复制到输出stream。
当你读第一个读取的大小,然后分配string,然后通过从stream中读取正确的字节数来填充它。
另一种方法是使用分隔符和转义,但是需要更多的代码,并且在序列化和反序列化方面都比较慢(但结果可以保持为人类可读)。
如果类包含任何外生数据( string
),则必须使用更复杂的序列化方法,而不是将类转换为char*
并将其写入文件。 而且你为什么会遇到分段错误是正确的。
我会做一个成员函数,它将采取一个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
来序列化和恢复你的类,如果你想要这个语法糖。
为什么不只是以下几点:
std::ofstream ofs; ... ofs << my_str;
接着:
std::ifstream ifs; ... ifs >> my_str;
/*! * 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; } };
我很久没有编码C ++,但也许你可以序列化一个char
数组。
然后,当你打开你的文件时,你的string
就会指向数组。
只是一个想法。