序列化概述
什么是序列化
程序员在编写应用程序的时候往往需要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络中的另一台计算机上以实现通讯。这个将程序数据转化成能被存储并传输的格式的过程被称为“序列化”(Serialization),而它的逆过程则可被称为“反序列化”(Deserialization)。
简单来说,序列化就是将对象实例的状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它根据流重构对象。这两个过程结合起来,可以轻松地存储和传输数据。例如,可以序列化一个对象,然后使用HTTP通过Internet在客户端和服务器之间传输该对象。
序列化包含侵入式序列化、非侵入式序列化、STL序列化
序列化:将对象变成字节流的形式传出去。
反序列化:从字节流恢复成原来的对象。
为什么要用序列化
1、将对象存储于硬盘上,便于以后反序列化使用
2、在网络上传送对象的字节序列
对网络传输而言,显而易见,序列化对象带来了便捷性和灵活性。另一方面,序列化也节省了开发时间。比如:有一个数据结构存储了大量数据,而里面存储的数据又是经过很多其它数据通过非常复杂的算法长时间计算生成的,因此生成该数据结构所用数据的时间要很久。生成该数据结构后又要用作其它的计算。那么在调试阶段,每次运行程序,都需要花费大量时间生成这个数据,无疑代价是非常大的。如果生成数据结构的算法不会变或不常变,那么就可以通过序列化技术生成数据结构数据存储到磁盘上,下次重新运行程序时只需要从磁盘上读取该对象数据即可,而不需要重新计算、生成这些数据,这样大大节省了我们的开发时间。
Boost.Serialization
Boost.Serialization可以创建或重建程序中的等效结构,并保存为二进制数据、文本数据、XML或者有用户自定义的其他文件。该库具有以下吸引人的特性:
- 代码可移植(实现仅依赖于ANSI C++)。
- 深度指针保存与恢复。
- 可以序列化STL容器和其他常用模版库。
- 数据可移植。
- 非入侵性。
侵入式序列化
侵入式序列化时,需要在class里面加入序列化的代码
- 先引用 boost 头文件
- 在类的声明中, 编写序列化函数,该函数的格式如下:
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
//version是版本号,可用于版本控制
{
ar& m_str;
}
- 类的实例化和赋值
- 定义一个序列化的对象和数据流的写入
boost::archive::text_oarchive text_oa(text_sstream);//文本方式
boost::archive::binary_oarchive binary_oa(binary_sstream);//二进制方式
binary_oa << info;//将对象info的序列化数据以二进制存储形式写入内存
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
class Student
{
friend class boost::serialization::access;
public:
Student(){}
~Student(){}
Student(int a,float b):id_(a), score_(b){}
inline int id() { return id_; }
inline float score() { return score_; }
private:
template<typename T>
void serialize(T& arch, const unsigned int version) {
arch& id_;
arch& score_;
}
int id_;
float score_;
};
#include<iostream>
#include"student.h"
using namespace std;
int main() {
std::ofstream out("archiv.txt");
boost::archive::text_oarchive oa(out);
Student stu(1, 2.3);
oa << stu;
out.close();
cout << "序列化完毕!" << endl;
std::ifstream in("archiv.txt");
boost::archive::text_iarchive ia(in);
ia >> stu;
cout << stu.id() << "--" << stu.score() << endl;
in.close();
system("pause");
return 0;
}
非侵入式序列化
如果class是早已存在的,且我们不想再改变class里面的代码时,这个时候,我们可以使用非侵入式的序列化。非侵入式序列化时,序列化函数需要访问数据成员,这就要求将class的数据成员暴露出来,即public,而不是private。其序列化的步骤和上面的侵入式序列化步骤一致。
Boost.Serialization的使用
txt文本格式序列化实例
- 使用<<和>>运算符完成序列化和反序列化
- 使用&运算符完成序列化和反序列化
XML格式序列化实例
步骤上与txt文本不同的地方在于,需要将数据打包到一个名为 BOOST_SERIALIZATION_NVP 的宏中。
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <iostream>
#include <fstream>
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
std::string s = "Hello World!\n";
oa & BOOST_SERIALIZATION_NVP(s);
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
std::string s;
ia & BOOST_SERIALIZATION_NVP(s);
std::cout << s << std::endl;
}
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<s>Hello World!</s>
</boost_serialization>
数组的序列化
过程与字符串类一样;但在恢复过程中,需要指定预期的数组大小:
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
int array1[] = {34, 78, 22, 1, 910};
oa & BOOST_SERIALIZATION_NVP(array1);
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
int restored[5]; // Need to specify expected array size
ia >> BOOST_SERIALIZATION_NVP(restored);
std::ostream_iterator<int> oi(std::cout, " ");
std::copy(a, a+5, oi);
}
int main()
{
save();
load();
}
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<array1>
<count>5</count>
<item>34</item>
<item>78</item>
<item>22</item>
<item>1</item>
<item>910</item>
</array1>
</boost_serialization>
STL集合的序列化
对于列表和向量,在将信息加载回来的过程中,不需要提供任何大小和范围信息:
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
float array[] = {34.2, 78.1, 22.221, 1.0, -910.88};
std::list<float> L1(array, array+5);
std::vector<float> V1(array, array+5);
oa & BOOST_SERIALIZATION_NVP(L1);
oa & BOOST_SERIALIZATION_NVP(V1);
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
std::list<float> L2;
ia >> BOOST_SERIALIZATION_NVP(L2); // No size/range needed
std::vector<float> V2;
ia >> BOOST_SERIALIZATION_NVP(V2); // No size/range needed
std::ostream_iterator<float> oi(std::cout, " ");
std::copy(L2.begin(), L2.end(), oi);
std::copy(V2.begin(), V2.end(), oi);
}
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<L1>
<count>5</count>
<item_version>0</item_version>
<item>34.200001</item>
<item>78.099998</item>
<item>22.221001</item>
<item>1</item>
<item>-910.88</item>
</L1>
<V1>
<count>5</count>
<item_version>0</item_version>
<item>34.200001</item>
<item>78.099998</item>
<item>22.221001</item>
<item>1</item>
<item>-910.88</item>
</V1>
</boost_serialization>
自定义类型序列化
typedef struct date {
unsigned int m_day;
unsigned int m_month;
unsigned int m_year;
} date;
侵入式版本:在类定义中定义一个名为 serialize 的方法:
template<class Archive>
void serialize(Archive& archive, const unsigned int version)
{
archive & BOOST_SERIALIZATION_NVP(m_day);
archive & BOOST_SERIALIZATION_NVP(m_month);
archive & BOOST_SERIALIZATION_NVP(m_year);
}
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <iostream>
#include <fstream>
typedef struct date {
unsigned int m_day;
unsigned int m_month;
unsigned int m_year;
date( int d, int m, int y) : m_day(d), m_month(m), m_year(y)
{}
date() : m_day(1), m_month(1), m_year(2000)
{}
friend std::ostream& operator << (std::ostream& out, date& d)
{
out << "day:" << d.m_day
<< " month:" << d.m_month
<< " year:" << d.m_year;
return out;
}
template<class Archive>
void serialize(Archive& archive, const unsigned int version)
{
archive & BOOST_SERIALIZATION_NVP(m_day);
archive & BOOST_SERIALIZATION_NVP(m_month);
archive & BOOST_SERIALIZATION_NVP(m_year);
}
} date;
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
date d(15, 8, 1947);
oa & BOOST_SERIALIZATION_NVP(d);
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
date dr;
ia >> BOOST_SERIALIZATION_NVP(dr);
std::cout << dr;
}
非侵入式版本:
namespace boost {
namespace serialization {
template<class Archive>
void serialize(Archive& archive, date& d, const unsigned int version)
{
archive & BOOST_SERIALIZATION_NVP(d.m_day);
archive & BOOST_SERIALIZATION_NVP(d.m_month);
archive & BOOST_SERIALIZATION_NVP(d.m_year);
}
} // namespace serialization
} // namespace boost
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<d class_id="0" tracking_level="0" version="0">
<d.m_day>15</d.m_day>
<d.m_month>8</d.m_month>
<d.m_year>1947</d.m_year>
</d>
</boost_serialization>
序列化版本控制
#include "boost/archive/binary_oarchive.hpp"
#include "boost/archive/binary_iarchive.hpp"
#include <iostream>
#include <sstream>
#include <fstream>
#define MAN 1
#define WOMAN 2
#define UNKNOW 3
class CPerson
{
public:
CPerson()
{
;
}
CPerson(size_t nBirthYear, std::string strName, size_t nGender)
: m_nBirthYear(nBirthYear), m_strName(strName), m_nGender(nGender)
{
;
}
~CPerson()
{
;
}
size_t GetBirYear() const
{
return m_nBirthYear;
}
std::string GetName() const
{
return m_strName;
}
size_t m_GetGender() const
{
return m_nGender;
}
private:
//要想序列化用户定义的类型 必须定义seriallze函数
//serialzation不应该被显示调用 因为它仅在序列化或者反序列化的时候调用
//应该声明为private的友元函数(不然boost::serialization访问不到)
friend class boost::serialization::access;
//serialize()提供了&操作符
//使用&操作符就不需要再serialize中区分是序列化还是反序列化了
template <typename Archive>
void serialize(Archive &ar, CPerson &p, const unsigned int version);
private:
size_t m_nBirthYear;
size_t m_nGender;
std::string m_strName;
};
template<typename Archive>
void CPerson::serialize(Archive & ar, CPerson &p, const unsigned int version)
{
ar & p.m_nBirthYear;
ar & p.m_strName;
/*版本1之后存储了以下变量 低版本则使用默认值*/
if (version >= 1)
{
ar & p.m_nGender;
}
else
{
p.m_nGender = UNKNOW;
}
}
BOOST_CLASS_VERSION(CPerson, 1);
void Save()
{
std::ofstream File("Data.bin");
boost::archive::binary_oarchive oa(File);
std::cout << "请输入名字" << std::endl;
std::string strName;
std::cin >> strName;
std::cout << "请输入出生年份" << std::endl;
size_t nBirthYear = 0;
std::cin >> nBirthYear;
std::cout << "请输入性别 1.男性 2.女性 3.未知" << std::endl;
size_t nGender = 0;
std::cin >> nGender;
CPerson p(nBirthYear, strName, nGender);
oa << p;
File.close();
}
void Load()
{
std::ifstream File("Data.bin");
boost::archive::binary_iarchive ia(File);
CPerson p;
ia >> p;
std::cout << "姓名:"<< p.GetName()
<< " 出生年份:" << p.GetBirYear()
<< " 性别代码:" <<p.m_GetGender()
<< std::endl;
File.close();
}
int main()
{
int nTemp = 0;
std::cout << "1.输入并存储 2.读取并显示" << std::endl;
std::cin >> nTemp;
switch (nTemp)
{
case 1:
Save();
break;
case 2:
Load();
break;
default:
break;
}
system("pause");
return 0;
}
版本控制的意义在于:随着class的参数的扩充,版本控制可以兼容旧版本的数据。version的值由宏BOOST_CLASS_VERSION指定,需要两个参数:一个是类名,一个是版本号。版本号默认为0,版本号只支持整型1个字节(0 - 255)。设置版本号之后,序列化会存储版本号,这样就能控制读取变量了。
#pragma once
#include"boost/archive/xml_iarchive.hpp"
#include"boost/archive/xml_oarchive.hpp"
#include"boost/serialization/vector.hpp"
#include"boost/serialization/list.hpp"
#include<fstream>
#include<iterator>
#include<algorithm>
class date
{
public:
date(int d,int m,int y):m_day(d),m_month(m),m_year(y){}
date() :m_day(1), m_month(1), m_year(1) {}
~date(){}
friend std::ostream& operator << (std::ostream& out, date& d) {
out << "d.m_day:" << d.m_day << "-d.m_month:" << d.m_month << "-d.m_year:" << d.m_year << std::endl;
return out;
}
private:
friend class boost::serialization::access;
template<typename T>
void serialize(T& archive, const unsigned int version) {
archive & BOOST_SERIALIZATION_NVP(m_day);
archive & BOOST_SERIALIZATION_NVP(m_month);
archive & BOOST_SERIALIZATION_NVP(m_year);
}
unsigned int m_day;
unsigned int m_month;
unsigned int m_year;
};
#include<iostream>
#include"serialization_cusdata.h"
using namespace std;
void arrsave() {
ofstream os("archieve.xml");
boost::archive::xml_oarchive oa(os);
int arr[] = {1,2,3,4,56,7,8};
oa & BOOST_SERIALIZATION_NVP(arr);
cout << "数组序列化完毕" << endl;
}
void vecsave() {
ofstream os("archievec.xml");
vector<int> vec = { 1,2,3,4,45,67,89 };
boost::archive::xml_oarchive oa(os);
oa & BOOST_SERIALIZATION_NVP(vec);
cout << "vecsave()序列化完毕" << endl;
}
void arrload() {
ifstream is("archieve.xml");
boost::archive::xml_iarchive ia(is);
int restroy[10];
ia & BOOST_SERIALIZATION_NVP(restroy);
ostream_iterator<int> oi(cout," ");
copy(restroy, restroy + 7, oi);
cout << "数组反序列化完毕" << endl;
}
void vecload() {
ifstream is("archievec.xml");
boost::archive::xml_iarchive ia(is);
vector<int> inp;
ia & BOOST_SERIALIZATION_NVP(inp);
ostream_iterator<int> oa(cout," ");
copy(inp.begin(), inp.end(), oa);
cout << "vecload反序列化完毕" << endl;
}
void serialization_cusdata_test() {
date da(8,3,2024);
ofstream os("cusdata_serialization.xml");
boost::archive::xml_oarchive oa(os);
oa & BOOST_SERIALIZATION_NVP(da);
cout << "serialization_cusdata_test()序列化完毕" << endl;
}
void serialization_cusdata_test_read() {
ifstream is("cusdata_serialization.xml");
boost::archive::xml_iarchive ia(is);
date da;
ia >> BOOST_SERIALIZATION_NVP(da);
cout << da;
}
int main() {
arrsave();
arrload();
vecsave();
vecload();
serialization_cusdata_test();
serialization_cusdata_test_read();
system("pause");
return 0;
}