zoukankan      html  css  js  c++  java
  • boost--序列化库serialization

    序列化可以把对象转化成一个字节流存储或者传输,在需要时再回复成与原始状态一致的等价对象。C++标准没有定义这个功能。boost.serialization以库的形式提供了这个功能,非常强大,可以序列化C++中各种类型,而且简单易用。

    boost.serialization库必须编译后才能使用。有关boost库的编译可以参考之前的文章windows下编译和安装boost库.

    serialization库把存档和类型的序列化完全分离开来,任意的数据类型都可以采用任意格式的存档保存。所以头文件被分别放在了两个目录下:
    <boost/archive/>目录的头文件处理序列化的存档表现形式
    <boost/serialization>目录里的头文件提供对各种数据类型的序列化能力。

    boost.serialization库位于名称空间boost.archive,在使用时必须根据需要包含特定的存档头文件和序列化头文件。
    例如:

    #include <boost/archive/text_oarchive.hpp> //文本格式输入存档
    #include <boost/archive/text_iarchive.hpp> //文本格式输出存档
    #include <boost/serialization/vector.hpp>  //vector的序列化实现头文件
    using namespace boost:archive;//打开名称空间
    

    serialization库的三个基本概念:

    存档

    存档在serialization库中表现为一系列的字节(不一定是ASCII或者二进制),它对应任意的C++对象,可以持久化保存并在某个时刻恢复成C++对象。

    根据存档格式分为:

    纯文本格式 text_iarchive text_oarchive

    xml格式 xml_iarchive xml_oarchive

    二进制格式 binary_iarchive binary_oarchive

    根据输入输出存档方向:
    输出存档(saving) : 把C++对象序列化为某种格式的字节流

    输入存档(loading) : 把某种格式的字节流反序列化为等价的C++对象。

    可序列化

    只有可序列化的C++类型才能够被序列化为字节流,保存到存档中或这从存档中恢复。
    (1) C++基本类型都是可序列化的,如bool、int、double、enum。
    (2) 字符串string、wstring是可序列化的
    (3) 自定义类型如果有特定形式的成员函数或者自由函数serialize()也是可序列化的。
    (4) 可序列化类型的数组也是可序列化的。
    (5) 可序列化类型的指针和引用也是可序列化的。
    注:serialization库支持标准库里定义的complex bitset valarray pair
    对标准容器支持,包括vector、deque、list、set、map,不支持stack,queue,priority_queue.

    序列化和反序列化

    序列化和反序列化是两个互逆的过程。
    序列化操作符: operator<< operator&
    反序列化操作符: operator>> operator&

    使用序列化

    //序列化
    ofstream ofs("serial.txt"); //输出文件流
    string str("boost serializaiton");
    
    {
    	boost::archive::text_oarchive oa(ofs);//文本输出存档连接到文件流
    	oa & str;	//序列化到输出存档
    }
    
    //反序列化	
    ifstream ifs("serial.txt");//文件输入流
    string istr;
    
    {
    	boost::archive::text_iarchive ia(ifs); //文本输入存档连接到文件流
    	ia & istr;//从输入文档反序列化
    }
    
    assert(istr == str);
    

    输出存档(如:text_oarchive)的构造函数需要使用一个输出流,创建输出存档后,就可以使用operator<<operator&向存档写入对象。

    输如存档(如:text_iarchive)构造时要求使用一个输入流,使用operator<<operator&执行反序列化。

    一个简单的序列化操作模版类:

    //BoostArchive.h
    #ifndef _BOOST_ARCHIVE_H_
    #define _BOOST_ARCHIVE_H_
    #include <list>
    #include <fstream>
    #include <string>
    #include <boost/archive/text_iarchive.hpp>
    #include <boost/archive/text_oarchive.hpp>
    
    using std::list;
    using std::ifstream;
    using std::ofstream;
    using std::string;
    
    template <class T>
    class BoostArchive
    {
    public:
    	typedef T entity_type;
    	typedef boost::archive::text_iarchive InputArchive;
    	typedef boost::archive::text_oarchive OutputArchive;
    
    	BoostArchive(const string & archive_file_path)
    		: _file_path_name(archive_file_path)
    		, _p_ofs(NULL)
    		, _p_output_archive(NULL)
    		, _entity_nums(0)
    	{
    		load_arvhive_info();
    	}
    	~BoostArchive()
    	{
    		close_output();
    	}
    	//存储一个对象,序列化
    	void store(const entity_type & entity);
    
    	//反序列化, 提取所有对象
    	bool restore(list<entity_type> & entitys);
    
    	size_t size() const
    	{
    		return _entity_nums;
    	}
    
    private:
    	void save_archive_info()	//保存已序列化的对象个数信息
    	{
    		ofstream ofs;
    		ofs.open(get_archive_info_file_path(),std::ios::out | std::ios::trunc);
    		if (ofs.is_open())
    		{
    			ofs << _entity_nums;
    		}
    		ofs.close();
    	}
    
    	void load_arvhive_info()//读取已序列化的对象个数信息
    	{
    		ifstream ifs;
    		ifs.open(get_archive_info_file_path(),std::ios_base::in);
    		if (ifs.is_open() && !ifs.eof())
    		{
    			int enity_num = 0;
    			ifs >> enity_num;
    			_entity_nums = enity_num;
    		}
    		ifs.close();
    	}
    
    	string get_archive_info_file_path()
    	{
    		return "boost_archive_info.meta";
    	}
    
    	void close_output()
    	{
    		if (NULL != _p_output_archive)
    		{
    			delete _p_output_archive;
    			_p_output_archive = NULL;
    			save_archive_info();
    		}
    		if (NULL != _p_ofs)
    		{
    			delete _p_ofs;
    			_p_ofs = NULL;
    		}
    	}
    
    private:
    	size_t _entity_nums;
    	string _file_path_name;
    	ofstream * _p_ofs;
    	OutputArchive * _p_output_archive;
    };
    
    template <class T>
    bool BoostArchive<T>::restore( list<entity_type> & entitys )
    {
    	close_output();
    	load_arvhive_info();
    	ifstream ifs(_file_path_name);
    	if (ifs)
    	{
    		InputArchive ia(ifs);
    		for (size_t cnt = 0; cnt < _entity_nums; ++cnt)
    		{
    			entity_type entity;
    			ia & entity;
    			entitys.push_back(entity);
    		}
    		return true;
    	}
    	return false;
    }
    
    template <class T>
    void BoostArchive<T>::store( const entity_type & entity )
    {
    	if (NULL == _p_output_archive)
    	{
    		_p_ofs = new ofstream(_file_path_name);
    		_p_output_archive = new OutputArchive(*_p_ofs);
    	}
    	(*_p_output_archive) & entity;
    	++_entity_nums;
    }
    
    #endif 
    

    自定义类型的序列化

    自定义类型可以使用两种方式实现可序列化:侵入式和非侵入式

    侵入式可序列化

    侵入式方式要求类必须实现如下形式的一个成员函数serialize():

    template<typename Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
    	...
    }
    

    第一个参数是被用于序列化或反序列化的存档,第二个参数是一个整型的版本号。在serialize函数内部,应当使用operator& 存取类的所有必要的数据成员。
    boost::serialization::access是一个辅助类,声明了一系列的静态成员函数间接调用自定义类的serialize(),存档通过它来完成对自定义类的序列化。一般在自定义类中声明boost::serialization::access为友元来授予访问权限,serialize()设置为private。

    侵入式序列化示例:

    //自定义类型的序列化 
    //侵入式
    class Student
    {
    public:
    	Student()
    		: _id(-1)
    		, _name("")
    	{}
    	Student(const int id, const string & name)
    		: _id(id)
    		, _name(name)
    	{}
    
    	void add_score(double score)
    	{
    		_scores.push_back(score);
    	}
    
    	double get_avg_score() const
    	{
    		return (_scores.size() == 0) ? 0 : 
    			(std::accumulate(_scores.begin(),_scores.end(),0) / _scores.size());
    	}
    
    	string get_student_msg() const
    	{
    		stringstream ss;
    		ss << "
    ID: " << _id << "  NAME: " << _name << "
    ";
    		for (auto iter = _scores.begin(); iter != _scores.end(); ++iter)
    		{
    			ss << *iter << " ";
    		}
    		ss << "
    AVG_SCORE: " << get_avg_score();
    		return ss.str();
    	}
    
    private:
    	friend boost::serialization::access;		//声明友元,授予访问权限
    	template<typename Archive>
    	void serialize(Archive & ar, const unsigned int version) //序列化函数
    	{
    		ar & _id;
    		ar & _name;
    		ar & _scores;
    	}
    private:
    	string _name;
    	int _id;
    	vector<double> _scores;
    	
    };
    

    测试代码(使用到了上面的辅助类BoostArchive):

    void TestArchive()
    {
    	Student s1(1,"cm");
    	s1.add_score(100);
    	s1.add_score(80);
    	s1.add_score(90);	
    
    	Student s2(2,"cj");
    	s2.add_score(100);
    	s2.add_score(90);
    	s2.add_score(90);
    
    	Student s3(3,"zj");
    	s3.add_score(100);
    	s3.add_score(60);
    	s3.add_score(90);
    
    	string archive_file_name("students.dat");
    	BoostArchive<Student> archive(archive_file_name);
    	archive.store(s1);	//序列化
    	archive.store(s2);
    	archive.store(s3);
    
    	list<Student> list1;
    	archive.restore(list1); //反序列化,恢复数据
    	for (auto iter = list1.cbegin(); iter != list1.end(); ++iter)
    	{
    		cout << iter->get_student_msg();
    	}
    }
    

    非侵入式可序列化

    侵入式可序列化的缺点是要修改类定义,添加一些代码。如果某个类定义是无法修改的,就只能使用非侵入的方式,定义一个如下形式的自由函数:

    //非侵入式
    namespace boost{
    namespace serialization{
    	template<typename Archive>
    	void serialize(Archive & ar, some_class & t, const unsigned int version)
    	{
    		...
    	}
    }
    }
    

    自由函数serialize()与成员函数serialize类似,多了一个自定义类型的参数t,在函数体内用它来完成序列化和反序列化。因为非侵入式不能访问类的私有成员,所以要求要被序列化的成员为public。
    其次为了方便编译器查找自由函数serialize,通常应该定义在名称空间boost::serialization,或boost::archive和自定义类型所在的名称空间。

    非侵入式序列化示例:

    struct Person
    {
    	Person()
    		:_id(-1)
    		, _name("")
    	{
    	}
    	Person(int id, const string & name)
    		:_id(id)
    		, _name(name)
    	{
    	}
    
    	string get_msg() const
    	{
    		stringstream ss;
    		ss << _id << "  " << _name;
    		return ss.str();
    	}
    
    	int _id;
    	string _name;
    };
    
    //非侵入式
    namespace boost{
    namespace serialization{
    	template<typename Archive>
    	void serialize(Archive & ar, Person & p, const unsigned int version)
    	{
    		ar & p._id;
    		ar & p._name;
    	}
    }
  • 相关阅读:
    socket (一)
    yield生成器及字符串的格式化
    python模块(json和pickle模块)
    python标准模块(time、datetime及hashlib模块)
    python标准模块(os及sys模块)
    python模块简介
    python --> 正则表达式
    python --> 递归 以及装饰器
    python基础知识(四)
    python基础知识(三)
  • 原文地址:https://www.cnblogs.com/cmranger/p/4772149.html
Copyright © 2011-2022 走看看