C++中如何实现序列化和反序列化?

news/2024/12/23 16:07:24 标签: c++, 服务器, 数据库

概念

在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 对象的姓名和年龄。

http://www.niftyadmin.cn/n/5796729.html

相关文章

[react 3种方法] 获取ant组件ref用ts如何定义?

获取ant的轮播图组件, 我用ts如何定义? Strongly Type useRef with ElementRef | Total TypeScript import React, { ElementRef } from react; const lunboRef useRef<ElementRef<typeof Carousel>>(null); <Carousel autoplay ref{lunboRef}> 这样就…

Vue.js 核心概念:模板、指令、数据绑定

Vue.js 核心概念&#xff1a;模板、指令、数据绑定 本文我们来聊一聊 Vue.js 的核心概念&#xff0c;重点讲解 Vue 中的 模板&#xff08;Template&#xff09;、指令&#xff08;Directives&#xff09; 和 数据绑定&#xff08;Data Binding&#xff09;。这些概念是 Vue.js…

JavaScript文件端点提取与安全分析:两种高效实用的方法

提取JS文件中的所有端点(Endpoints) JavaScript文件中包含了大量的信息,对于安全研究人员来说,提取这些文件中的API端点是发现潜在漏洞的重要环节之一。在本篇文章中,我们将介绍两种高效提取JavaScript文件端点的方法。以下方法主要应用于渗透测试场景,尤其是针对目标域…

深度学习实战之超分辨率算法(tensorflow)——ESPCN

espcn原理算法请参考上一篇论文&#xff0c;这里主要给实现。 数据集如下&#xff1a;尺寸相等即可 针对数据集&#xff0c;生成样本代码preeate_data.py import imageio from scipy import misc, ndimage import numpy as np import imghdr import shutil import os import…

<C#> 无法加载 DLL“xxx.dll”: 找不到指定的模块。 (异常来自 HRESULT:0x8007007E)。

1、报错异常 无法加载 DLL“xxx.dll”: 找不到指定的模块。 (异常来自 HRESULT:0x8007007E)。 2、原因分析 所引用的DLL也引用了其他DLL&#xff0c;但是在本次引入中并未引入。所引用的DLL所在路径不对&#xff0c;尝试放在.exe所在目录&#xff1b;DLL生成的平台(x64/x86/…

Hive SQL 查询所有函数

-- 显示所有的函数 show functions; -- 对函数year进行解释 desc function year; -- 对函数year进行详细解释&#xff0c;并举例说明 desc function extended year;– 对函数year进行解释 desc function year; – 对函数year进行详细解释&#xff0c;并举例说明 desc functio…

Netty解决粘包半包问题

1.定长&#xff0c;每次读取固定的数据量 ChannelPipeline pipeline ch.pipeline(); pipeline.addLast(new FixedLengthFrameDecoder(10)); // 每条消息长度固定为10字节 pipeline.addLast(new YourBusinessHandler()); 每条消息长度固定&#xff0c;接收端读取固定字节数作…

HttpPrinter Web打印控件使用教程:免费且功能全面

web打印控件完全免费 最好的免费的WEB打印控件,HttpPrinter是专业Web打印控件,类型为ActiveX插件,用它既可裁剪输出页面内容&#xff0c;又可用程序代码生成复杂打印页。该控件功能异常强大&#xff0c;却简单易用。 Web打印控件是实现网页打印功能的重要组成部分&#xff0c…