Подтвердить что ты не робот

Сериализация класса, содержащего std::string

Я не эксперт на С++, но в прошлом я делал сериализацию несколько раз. К сожалению, на этот раз я пытаюсь сериализовать класс, содержащий std::string, который, как я понимаю, в значительной степени похож на сериализацию указателя.

Я могу записать класс в файл и снова прочитать его. Все поля int прекрасны, но поле std::string дает ошибку "адрес за пределами границ", по-видимому, потому, что указывает на данные, которые больше не существуют.

Существует ли стандартное обходное решение? Я не хочу возвращаться к массивам char, но, по крайней мере, я знаю, что они работают в этой ситуации. Я могу предоставить код, если это необходимо, но я надеюсь, что я хорошо объяснил свою проблему.

Я сериализую, отбрасывая класс в char * и записывая его в файл с файлом fstream. Чтение, конечно, только наоборот.

4b9b3361

Ответ 1

Я сериализую, выставив класс в 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;}

Ответ 2

Просто запись двоичного содержимого объекта в файл не только не переносима, но, как вы признали, не работает для данных указателя. В основном у вас есть два варианта: либо вы пишете настоящую библиотеку сериализации, которая правильно обрабатывает std:: strings, например. используя c_str(), чтобы вывести фактическую строку в файл, или вы используете отличный увеличить сериализацию. Если это вообще возможно, я бы рекомендовал последнее, вы можете сериализовать с помощью простого кода, подобного этому:

#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 работает для сериализации и десериализации данных в зависимости от того, как вы это называете. Дополнительную информацию см. В документации.

Ответ 3

Самый простой метод сериализации для строк или других блоков с переменным размером - это сериализовать сначала размер при сериализации целых чисел, а затем просто скопировать содержимое в выходной поток.

При чтении вы сначала читаете размер, затем выделяете строку, а затем заполняете ее, читая правильное количество байтов из потока.

Альтернативой является использование разделителя и экранирования, но требуется больше кода и медленнее как при сериализации, так и при десериализации (однако результат может быть сохранен для чтения человеком).

Ответ 4

Вам придется использовать более сложный метод сериализации, чем приведение класса к 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 для сериализации и восстановления вашего класса, если вы хотите использовать этот синтаксический сахар.

Ответ 5

Почему не просто что-то вроде:

std::ofstream ofs;
...

ofs << my_str;

а затем:

std::ifstream ifs;
...

ifs >> my_str; 

Ответ 6

/*!
 * 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;
     }
};

Ответ 7

Я не кодировал С++ в течение длительного времени, но, возможно, вы могли бы сериализовать массив char.

Затем, когда вы открываете файл, ваш string будет просто указывать на массив.

Просто идея.