Сериализация classа, который содержит std :: string
Я не эксперт на C ++, но в прошлом я делал сериализацию несколько раз. К сожалению, на этот раз я пытаюсь сериализовать class, который содержит std :: string, который, как я понимаю, в значительной степени похож на сериализацию указателя.
Я могу записать class в файл и снова прочитать его. Все int-поля прекрасны, но поле std :: string дает ошибку «адрес за пределами границ», по-видимому, потому, что указывает на данные, которых больше нет.
Существует ли стандартное обходное решение для этого? Я не хочу возвращаться к массивам символов, но, по крайней мере, я знаю, что они работают в этой ситуации. Я могу предоставить код, если это необходимо, но я надеюсь, что я хорошо объяснил свою проблему.
- Использование jQuery для захвата содержимого из iframe CKEditor
- Исключение «Исключительная привязка к реляционной ссылке» с JSON.Net
- Json.Net: свойство Serialize / Deserialize в качестве значения, а не как объект
- Почему java.util.Optional не является Serializable, как сериализовать объект с такими полями
- Как можно полиморфная десериализация Json String с использованием Java и библиотеки Jackson?
Я сериализую, выставив class в char * и записывая его в файл с файлом fstream. Чтение, конечно, только наоборот.
- jQuery serializeArray не включает кнопку отправки, которая была нажата
- Сериализация / десериализация пользовательской коллекции с дополнительными свойствами с помощью Json.Net
- Использовать имя classа в качестве корневого ключа для сериализации JSON Jackson
- Когда и почему сущности JPA должны реализовывать интерфейс Serializable?
- Задача не сериализуема: java.io.NotSerializableException при вызове функции закрытие только для classов не объектов
- Как десериализовать JSON с двойными именами свойств в одном и том же объекте
- Использование JsonConvert.DeserializeObject для десериализации Json в class C # POCO
- Серийный цикл ActiveRecord с использованием JSON вместо YAML
Я сериализую, выставив class в char * и записывая его в файл с файлом fstream. Чтение, конечно, только наоборот.
К сожалению, это работает только до тех пор, пока нет указателей. Возможно, вы захотите присвоить своим classам 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 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;}
Просто запись двоичного содержимого объекта в файл не только не переносима, но, как вы признали, не работает для данных указателя. В основном у вас есть два варианта: либо вы пишете настоящую библиотеку сериализации, которая правильно обрабатывает std :: strings, например, используя c_str () для вывода фактической строки в файл, или вы используете отличную библиотеку сериализации boost . Если это вообще возможно, я бы рекомендовал последнее, вы можете сериализовать с помощью простого кода, подобного этому:
#include #include #include class A { private: std::string s; public: template void serialize(Archive& ar, const unsigned int version) { ar & s; } };
Здесь serialize
функции работает для сериализации и десериализации данных в зависимости от того, как вы это называете. Дополнительную информацию см. В документации.
Самый простой способ сериализации для строк или других блоков с переменным размером состоит в том, чтобы сначала сериализовать размер по мере сериализации целых чисел, а затем просто скопировать содержимое в выходной stream.
При чтении вы сначала читаете размер, затем выделяете строку, а затем заполняете ее, читая правильное количество байтов из streamа.
Альтернативой является использование разделителя и экранирование, но требуется больше кода и медленнее как при сериализации, так и при десериализации (однако результат может быть сохранен для чтения человеком).
Вам придется использовать более сложный метод сериализации, чем приведение classа в char*
и запись его в файл, если ваш class содержит любые экзогенные данные ( 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>>
для работы с ostream
и ostream
для сериализации и восстановления вашего classа, если вы хотите использовать этот синтаксический сахар.
Почему не просто что-то вроде:
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 buff(new char[size]); // custom smart pointer is.read(reinterpret_cast(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(&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(&size), sizeof(uint32_t)); m_ID = UReadBinaryString::read(is, size); return is; } };
Я не кодировал C ++ в течение длительного времени, но, возможно, вы могли бы сериализовать массив char
.
Затем, когда вы открываете файл, ваша string
будет просто указывать на массив.
Просто идея.