概念
在C++中,序列化和反序列化通常需要通过自定义代码将对象的状态转换为字节流,或者将字节流转换回对象。这可以通过文件操作、网络传输或其他形式的存储来实现。
使用简单的文件流
我们可以通过 ofstream 和 ifstream 类来实现基本的序列化与反序列化。假设我们有一个简单的 Person 类。
#include <iostream>
#include <fstream>
#include <string>
class Person {
public:
std::string name;
int age;
Person() : name(""), age(0) {} // 默认构造函数
Person(const std::string& n, int a) : name(n), age(a) {}
// 序列化
void serialize(std::ofstream& out) {
out << name << std::endl; // 写入姓名
out << age << std::endl; // 写入年龄
}
// 反序列化
void deserialize(std::ifstream& in) {
std::getline(in, name); // 读取姓名
in >> age; // 读取年龄
in.ignore(); // 忽略换行符
}
};
int main() {
Person person("Alice", 30); // 创建一个 Person 对象
// 序列化
std::ofstream outFile("person.txt"); // 创建文件输出流
person.serialize(outFile); // 调用序列化方法
outFile.close(); // 关闭输出流
// 反序列化
Person loadedPerson; // 创建一个新的 Person 对象
std::ifstream inFile("person.txt"); // 创建文件输入流
loadedPerson.deserialize(inFile); // 调用反序列化方法
inFile.close(); // 关闭输入流
std::cout << "Name: " << loadedPerson.name << ", Age: " << loadedPerson.age << std::endl; // 输出反序列化结果
return 0;
}
代码解析
1.序列化方法
void serialize(std::ofstream& out) {
out << name << std::endl; // 写入姓名
out << age << std::endl; // 写入年龄
}
- serialize 方法接收一个 ofstream 对象作为参数,用于将 Person 对象的数据写入文件。
- std::endl 用于在每个数据后插入换行符,使文件内容更清晰。
2.反序列化方法
void deserialize(std::ifstream& in) {
std::getline(in, name); // 读取姓名
in >> age; // 读取年龄
in.ignore(); // 忽略换行符
}
- deserialize 方法接收一个 ifstream 对象作为参数,通过该对象读取文件内容。
- 使用 std::getline 读取姓名,in >> age 读取年龄。
- in.ignore() 用于忽略读取 age 之后的换行符,以便下一次读取时能正确读取下一行数据。
3.main函数
int main() {
Person person("Alice", 30); // 创建一个 Person 对象
// 序列化
std::ofstream outFile("person.txt"); // 创建文件输出流
person.serialize(outFile); // 调用序列化方法
outFile.close(); // 关闭输出流
// 反序列化
Person loadedPerson; // 创建一个新的 Person 对象
std::ifstream inFile("person.txt"); // 创建文件输入流
loadedPerson.deserialize(inFile); // 调用反序列化方法
inFile.close(); // 关闭输入流
std::cout << "Name: " << loadedPerson.name << ", Age: " << loadedPerson.age << std::endl; // 输出反序列化结果
return 0;
}
- 创建一个 Person 对象并为其赋值。
- 使用 ofstream 打开一个文本文件进行写入,通过调用 serialize 方法将数据写入文件。
- 使用 ifstream 打开同一个文件进行读取,通过调用 deserialize 方法获得文件中的数据。
- 最后,输出反序列化 Person 对象的姓名和年龄。
使用二进制文件
如果您想要更高效的存储格式,可以选择二进制序列化。
#include <iostream>
#include <fstream>
#include <string>
class Person {
public:
std::string name; // 姓名
int age; // 年龄
Person() : name(""), age(0) {} // 默认构造函数
Person(const std::string& n, int a) : name(n), age(a) {} // 带参数的构造函数
// 序列化为二进制
void serialize(std::ofstream& out) {
size_t nameLength = name.size(); // 获取姓名的长度
out.write(reinterpret_cast<char*>(&nameLength), sizeof(nameLength)); // 写入姓名长度
out.write(name.data(), nameLength); // 写入姓名内容
out.write(reinterpret_cast<char*>(&age), sizeof(age)); // 写入年龄
}
// 反序列化从二进制
void deserialize(std::ifstream& in) {
size_t nameLength; // 存储姓名长度
in.read(reinterpret_cast<char*>(&nameLength), sizeof(nameLength)); // 读取姓名长度
name.resize(nameLength); // 调整姓名字符串的大小以匹配长度
in.read(&name[0], nameLength); // 读取姓名内容
in.read(reinterpret_cast<char*>(&age), sizeof(age)); // 读取年龄
}
};
int main() {
Person person("Alice", 30); // 创建一个 Person 对象
// 序列化
std::ofstream outFile("person.bin", std::ios::binary); // 创建二进制文件输出流
person.serialize(outFile); // 调用序列化方法
outFile.close(); // 关闭输出流
// 反序列化
Person loadedPerson; // 创建一个新的 Person 对象
std::ifstream inFile("person.bin", std::ios::binary); // 创建二进制文件输入流
loadedPerson.deserialize(inFile); // 调用反序列化方法
inFile.close(); // 关闭输入流
std::cout << "Name: " << loadedPerson.name << ", Age: " << loadedPerson.age << std::endl; // 输出反序列化结果
return 0;
}
代码解析
1.序列化方法
void serialize(std::ofstream& out) {
size_t nameLength = name.size(); // 获取姓名的长度
out.write(reinterpret_cast<char*>(&nameLength), sizeof(nameLength)); // 写入姓名长度
out.write(name.data(), nameLength); // 写入姓名内容
out.write(reinterpret_cast<char*>(&age), sizeof(age)); // 写入年龄
}
- serialize 方法将对象的状态写入二进制文件。
- 首先,获取 name 字符串的长度,然后将其以二进制形式写入文件。
- 其次,直接写入 name 字符串的内容。
- 最后,写入 age 整数。使用 reinterpret_cast 将 size_t 类型的指针转换为 char*,以便写入二进制格式。
2.反序列化方法
void deserialize(std::ifstream& in) {
size_t nameLength; // 存储姓名长度
in.read(reinterpret_cast<char*>(&nameLength), sizeof(nameLength)); // 读取姓名长度
name.resize(nameLength); // 调整姓名字符串的大小以匹配长度
in.read(&name[0], nameLength); // 读取姓名内容
in.read(reinterpret_cast<char*>(&age), sizeof(age)); // 读取年龄
}
- deserialize 方法从二进制文件中读取对象的状态。
- 使用 read 方法读取姓名长度,并调整 name 字符串的大小以匹配读取的长度。
- 然后读取姓名内容,最后读取年龄。
3.main函数
int main() {
Person person("Alice", 30); // 创建一个 Person 对象
// 序列化
std::ofstream outFile("person.bin", std::ios::binary); // 创建二进制文件输出流
person.serialize(outFile); // 调用序列化方法
outFile.close(); // 关闭输出流
// 反序列化
Person loadedPerson; // 创建一个新的 Person 对象
std::ifstream inFile("person.bin", std::ios::binary); // 创建二进制文件输入流
loadedPerson.deserialize(inFile); // 调用反序列化方法
inFile.close(); // 关闭输入流
std::cout << "Name: " << loadedPerson.name << ", Age: " << loadedPerson.age << std::endl; // 输出反序列化结果
return 0;
}
- 创建一个 Person 对象并初始化其属性。
- 创建一个 ofstream 对象,用于输出到二进制文件 (person.bin)。
- 调用 serialize 方法将 person 对象的数据写入文件。
- 创建一个新的 Person 对象 loadedPerson 来读取存储的数据。
- 创建一个 ifstream 对象来读取二进制文件,并调用 deserialize 方法。
- 最后,打印 loadedPerson 对象的姓名和年龄。
使用第三方库
对于复杂的序列化需求,您可以使用一些第三方库,如 Boost.Serialization 或 protobuf。这类库提供了更丰富的功能和更好的处理能力。
使用 Boost.Serialization 示例(简要)
首先,您需要安装 Boost 库,并确保包含正确的头文件。
#include <iostream>
#include <fstream>
#include <string>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/int.hpp>
class Person {
public:
std::string name; // 姓名
int age; // 年龄
Person() : name(""), age(0) {} // 默认构造函数
Person(const std::string& n, int a) : name(n), age(a) {} // 带参数的构造函数
// 序列化和反序列化
template<class Archive>
void serialize(Archive& ar, const unsigned int version) {
ar & name; // 使用 & 运算符来序列化姓名
ar & age; // 使用 & 运算符来序列化年龄
}
};
int main() {
Person person("Alice", 30); // 创建一个 Person 对象
// 序列化
{
std::ofstream ofs("person.txt"); // 创建文件输出流
boost::archive::text_oarchive oa(ofs); // 创建文本序列化对象
oa << person; // 序列化 person 对象
}
// 反序列化
Person loadedPerson; // 创建一个新的 Person 对象
{
std::ifstream ifs("person.txt"); // 创建文件输入流
boost::archive::text_iarchive ia(ifs); // 创建文本反序列化对象
ia >> loadedPerson; // 反序列化到 loadedPerson 对象
}
std::cout << "Name: " << loadedPerson.name << ", Age: " << loadedPerson.age << std::endl; // 输出反序列化结果
return 0;
}
代码解析
1.引入头文件
#include <iostream>
#include <fstream>
#include <string>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/int.hpp>
- #include 、#include 、#include :用于标准输入输出和字符串处理。
- #include <boost/archive/text_oarchive.hpp> 和
#include <boost/archive/text_iarchive.hpp>:用于文本序列化和反序列化的 Boost 头文件。 - #include <boost/serialization/string.hpp> 和#include<boost/serialization/int.hpp>:用于序列化 std::string 和 int 类型。
2.定义Person类
class Person {
public:
std::string name; // 姓名
int age; // 年龄
Person() : name(""), age(0) {} // 默认构造函数
Person(const std::string& n, int a) : name(n), age(a) {} // 带参数的构造函数
// 序列化和反序列化
template<class Archive>
void serialize(Archive& ar, const unsigned int version) {
ar & name; // 使用 & 运算符来序列化姓名
ar & age; // 使用 & 运算符来序列化年龄
}
};
- Person 类包含两个属性:姓名 (name) 和年龄 (age)。
- 提供默认构造函数和带参数的构造函数。
- serialize 方法模板接收一个 Archive 对象(用于序列化或反序列化),通过使用 & 运算符来实现序列化。
3.main函数
int main() {
Person person("Alice", 30); // 创建一个 Person 对象
// 序列化
{
std::ofstream ofs("person.txt"); // 创建文件输出流
boost::archive::text_oarchive oa(ofs); // 创建文本序列化对象
oa << person; // 序列化 person 对象
}
- 首先创建一个 Person 对象并初始化它。
- 开始一个新的作用域以限制文件输出流的生命周期。
- 创建一个 ofstream 对象并连接到一个文本文件(person.txt)。
- 创建 boost::archive::text_oarchive 对象用于序列化,传入文件输出流。
- 使用 oa << person; 将 person 对象序列化到文件中。
4.反序列化
// 反序列化
Person loadedPerson; // 创建一个新的 Person 对象
{
std::ifstream ifs("person.txt"); // 创建文件输入流
boost::archive::text_iarchive ia(ifs); // 创建文本反序列化对象
ia >> loadedPerson; // 反序列化到 loadedPerson 对象
}
std::cout << "Name: " << loadedPerson.name << ", Age: " << loadedPerson.age << std::endl; // 输出反序列化结果
return 0;
}
- 创建一个新 Person 对象 loadedPerson 用于存储反序列化的数据。
- 开始另一个作用域以限制文件输入流的生命周期。
- 创建一个 ifstream 对象并连接到之前的文本文件(person.txt)。
- 创建 boost::archive::text_iarchive 对象用于反序列化,将文件输入流作为参数传入。
- 使用 ia >> loadedPerson; 将文件中的数据反序列化到 loadedPerson 对象中。
- 最后,打印反序列化后的 loadedPerson 对象的姓名和年龄。