序列化包含 std::string 的类

发布于 2024-11-29 21:39:14 字数 355 浏览 2 评论 0 原文

我不是 C++ 专家,但我过去已经连载过几次。不幸的是,这次我试图序列化一个包含 std::string 的类,我理解这与序列化指针非常相似。

我可以将类写入文件并再次读回。所有 int 字段都很好,但 std::string 字段给出“地址越界”错误,大概是因为它指向不再存在的数据。

对此有标准的解决方法吗?我不想回到 char 数组,但至少我知道它们在这种情况下可以工作。如果需要,我可以提供代码,但我希望我已经很好地解释了我的问题。

我通过将类转换为 char* 并使用 std::fstream 将其写入文件来进行序列化。读书当然正好相反。

I'm not a c++ expert but I've serialized things a couple of times in the past. Unfortunately this time I'm trying to serialize a class which contains an std::string, which I understand is pretty much like serializing a pointer.

I can write out the class to a file and read it back in again. All int fields are fine, but the std::string field gives an "address out of bounds" error, presumably because it points to data which is no longer there.

Is there a standard workaround for this? I don't want to go back to char arrays, but at least I know they work in this situation. I can provide code if necessary, but I'm hoping I've explained my problem well.

I'm serializing by casting the class to a char* and writing it to a file with std::fstream. Reading of course is just the reverse.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

远昼 2024-12-06 21:39:14

我通过将类转换为 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;
}

可能还希望重载流运算符以便于使用。

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;}

I'm serializing by casting the class to a char* and writing it to a
file with fstream. Reading of course is just the reverse.

Unfortunately, this only works as long as there are no pointers involved. You might want to give your classes void MyClass::serialize(std::ostream) and void MyClass::deserialize(std::ifstream), and call those. For this case, you'd want

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;
}

You may also want to overload the stream operators for easier use.

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;}
七颜 2024-12-06 21:39:14

简单地将对象的二进制内容写入文件不仅不可移植,而且正如您所认识到的那样,对于指针数据也不起作用。您基本上有两个选择:要么编写一个真正的序列化库,它可以通过使用 c_str() 将实际字符串输出到文件,或者您使用优秀的 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 用于序列化和反序列化数据,具体取决于您如何调用它。请参阅文档以获取更多信息。

Simply writing the binary contents of an object into a file is not only unportable but, as you've recognized, doesn't work for pointer data. You basically have two options: either you write a real serialization library, which handles std::strings properly by e.g. using c_str() to output the actual string to the file, or you use the excellent boost serialization library. If at all possible, I'd recommend the latter, you can then serialize with a simple code like this:

#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;
        }
};

Here, the function serialize works for serializing and deserializing the data, depending on how you call it. See the documentation for more information.

哑剧 2024-12-06 21:39:14

对于字符串或其他大小可变的 blob,最简单的序列化方法是在序列化整数时首先序列化大小,然后将内容复制到输出流。

读取时,首先读取大小,然后分配字符串,然后通过从流中读取正确的字节数来填充它。

另一种方法是使用分隔符和转义,但需要更多代码,并且序列化和反序列化速度较慢(但结果可以保持人类可读)。

The easiest serialization method for strings or other blobs with variable size is to serialize first the size as you serialize integers, then just copy the content to the output stream.

When reading you first read the size, then allocate the string and then fill it by reading the correct number of bytes from the stream.

An alternative is to use a delimiter and escaping, but requires more code and is slower both on serialization and deserialization (however the result can be kept human readable).

我爱人 2024-12-06 21:39:14

如果您的类包含任何外源数据(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>> 来与 istreamostream 一起使用 进行序列化和如果您想要语法糖,也可以恢复您的课程。

You'll have to use a more complicated method of serialization than casting a class to a char* and writing it to a file if your class contains any exogenous data (string does). And you're correct about why you're getting a segmentation fault.

I would make a member function that would take an fstream and read in the data from it as well as an inverse function which would take an fstream and write it's contents to it to be restored later, like this:

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);

You can also define operator<< and operator>> to work with istream and ostream to serialize and restore your class as well if you want that syntactic sugar.

む无字情书 2024-12-06 21:39:14

为什么不只是类似以下内容:

std::ofstream ofs;
...

ofs << my_str;

然后:

std::ifstream ifs;
...

ifs >> my_str; 

Why not just something along the lines of:

std::ofstream ofs;
...

ofs << my_str;

and then:

std::ifstream ifs;
...

ifs >> my_str; 
遮了一弯 2024-12-06 21:39:14
/*!
 * 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;
     }
};
/*!
 * 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;
     }
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文