C++ 序列化

本文介绍了一种利用C++ STL实现程序数据序列化的方法,包括单类型数据及多类型数据的序列化处理,以及如何处理包含指针的容器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

转自:http://blog.youkuaiyun.com/pandaxcl/archive/2006/04/03/649682.aspx
#if 0

    在用C++编写应用程序的过程中,经常涉及到序列化的问题,但是序列化的问题通常都
会有非常繁琐的过程代码需要书写,本文中就是通过简单的步骤实现了程序的序列化问题
,简单直接,和其它的序列化方案有着很大的不同。

    首先来看看简单的数据写入文件和从文件读入数据的代码:

    特别注解:本人特别喜欢用STL来书写代码,一方面是便于移植,但是另一方却是在于
用STL书写的代码简单直接,可读性好。如果还不熟悉STL,本文则不大适合你:)

#endif
#if CODE1
////////////////////////////////////////////////////////////////////////////////
//模拟程序序列化的简单代码
#include <iostream>//cout
#include <fstream>//ofstream,ifstream
#include <vector>//vector
#include <iterator>//ostream_iterator,istream_iterator,back_inserter
#include <numeric>//partial_sum
#include <algorithm>//copy
#include <string>
#include <sstream>
using namespace std;//简化代码的书写,经std名字空间成为默认名字空间
int main()
{
    {//从程序序列化到文件
        vector<int> v(5,1);//[1,1,1,1,1]
        partial_sum(v.begin(),v.end(),v.begin());//[1,2,3,4,5]
        ofstream out("data.txt");//生成文件输出流
        //将数组v中的数据全部输出到文件流中,这种操作在C++中成为文件操作
        //在这里暂时称为序列化到文件操作。实际上这里为了简单序列化的格式
        //为文本文件格式。如果需要其它的格式完全可以通过自定义输出流游标
        //的方式,或者重载运算符operator<<和operator>>实现不同的序列化格
        //式。可以参见本人的其它相关文档。
        copy(v.begin(),v.end(),ostream_iterator<int>(out," "));
    }
    {//从文件序列化到程序
        vector<int> v;//模拟应用程序中数据
        ifstream in("data.txt");//建立输入流
        //下面的这行代码从文件中提取数据到v中,模拟了应用程序的序列化过程
        copy(istream_iterator<int>(in),istream_iterator<int>(),back_inserter(v));
        //下面的这行代码仅仅只是为了显示是否真的被序列化到了程序中
        copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));
    }
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
//该程序的输出如下:
/*******************************************************************************
1 2 3 4 5
*******************************************************************************/
//data.txt中的内容如下:
/*******************************************************************************
1 2 3 4 5
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#endif//CODE1
#if 0
    很明显我们在应用程序中保存了数据的类型信息,如:文件中的[1,2,3,4,5]的类型“
int”信息是保存在程序中的,这一点是和其它的序列化方案有着极大的不同,用过MFC的
都知道,MFC中为了实现序列化功能,必须将所有的类型信息和数据信息同时保存在文件中
,虽然都是以二进制格式的形式,但是通过二进制编辑器可以很容易的看出你面保存的类
型信息字符串,这一点是有安全隐患的。所以从安全和保密上讲,本文中的方式是MFC序列
化机制所不能够做到的。从代码简洁程度和序列化文件的格式简单性来讲,本文中的方案
也绝对不是MFC的序列化所能够轻易做到的。在考察Boost的serialize库的时候,发现文件
中也保存了类型信息。
    讨论到了这里,你也许会说,这里只有一种类型的数据,如果类型多了,还能够用这
种方式实现序列化么。可以非常肯定的告诉你,我已经成功的使用过这种方式实现了序列
化了。下面来看一个非常直接的例子:
#endif
#if CODE2
////////////////////////////////////////////////////////////////////////////////
//模拟程序序列化的简单代码
#include <iostream>//cout
#include <fstream>//ofstream,ifstream
#include <sstream>//istringstream
#include <string>//string
#include <vector>//vector
#include <iterator>//ostream_iterator,istream_iterator,back_inserter
#include <numeric>//partial_sum
#include <algorithm>//copy
using namespace std;
const char delimiter = '*';//用来分隔多种数据类型的分隔符
int main()
{
    {//从程序序列化到文件
        //v1,v2,v3,v4,v5用来模拟应用程序中的许多类型的数据,当然类型信息可以
        //是任意的类型,当然需要为每一种类型书写各自的operator<<和operator>>
        //运算符啦!具体的情况具体分析,熟悉STL的人自然可以根据本文中的各个
        //STL算法和容器的要求写出必须的函数和操作符了。
        vector<int>     v1(3,1);//3个1
        vector<short>   v2(4,2);//4个2
        vector<long>    v3(5,3L);//5个3
        vector<float>   v4(6,4.1);//6个4.1
        vector<double>  v5(7,5.2);//7个5.2
        //一下的代码用来产生不同的类型序列
        partial_sum(v1.begin(),v1.end(),v1.begin());//[1,2,3]
        partial_sum(v2.begin(),v2.end(),v2.begin());//[2,4,6,8]
        partial_sum(v3.begin(),v3.end(),v3.begin());//[3L,6L,9L,12L,15L]
        partial_sum(v4.begin(),v4.end(),v4.begin());//[4.1,8.2,12.3,16.4,20.5,24.6]
        partial_sum(v5.begin(),v5.end(),v5.begin());//[5.2,10.4,15.6,20.8,26.0,31.2,36.4]
        ofstream out("data.txt");//生成文件输出流
        //将数组v中的数据全部输出到文件流中,这种操作在C++中成为文件操作
        //在这里暂时称为序列化到文件操作。实际上这里为了简单序列化的格式
        //为文本文件格式。如果需要其它的格式完全可以通过自定义输出流游标
        //的方式,或者重载运算符operator<<和operator>>实现不同的序列化格
        //式。可以参见本人的其它相关文档。
        //向文件中依次序列化所有的应用程序数据
        copy(v1.begin(),v1.end(),ostream_iterator<int   >(out," "));
        out << delimiter << endl;//输出分隔符分开不同的数据类型
        copy(v2.begin(),v2.end(),ostream_iterator<short >(out," "));
        out << delimiter << endl;//输出分隔符分开不同的数据类型
        copy(v3.begin(),v3.end(),ostream_iterator<long  >(out," "));
        out << delimiter << endl;//输出分隔符分开不同的数据类型
        copy(v4.begin(),v4.end(),ostream_iterator<float >(out," "));
        out << delimiter << endl;//输出分隔符分开不同的数据类型
        copy(v5.begin(),v5.end(),ostream_iterator<double>(out," "));
    }
    {//从文件序列化到程序
        //v1,v2,v3,v4,v5用来模拟应用程序中的许多类型的数据
        vector<int>     v1;
        vector<short>   v2;
        vector<long>    v3;
        vector<float>   v4;
        vector<double>  v5;
        ifstream in("data.txt");//建立文件输入流
        //从文件中依次提取出序列化到文件的所有的数据
        copy(istream_iterator<int   >(in),istream_iterator<int   >(),back_inserter(v1));
        in.clear();in.ignore(10,delimiter);//让流恢复状态继续提取数据
        copy(istream_iterator<short >(in),istream_iterator<short >(),back_inserter(v2));
        in.clear();in.ignore(10,delimiter);//让流恢复状态继续提取数据
        copy(istream_iterator<long  >(in),istream_iterator<long  >(),back_inserter(v3));
        in.clear();in.ignore(10,delimiter);//让流恢复状态继续提取数据
        copy(istream_iterator<float >(in),istream_iterator<float >(),back_inserter(v4));
        in.clear();in.ignore(10,delimiter);//让流恢复状态继续提取数据
        copy(istream_iterator<double>(in),istream_iterator<double>(),back_inserter(v5));
        //下面的5行代码仅仅只是为了显示是否真的被序列化到了程序中
        copy(v1.begin(),v1.end(),ostream_iterator<int   >(cout," "));cout<<endl;
        copy(v2.begin(),v2.end(),ostream_iterator<short >(cout," "));cout<<endl;
        copy(v3.begin(),v3.end(),ostream_iterator<long  >(cout," "));cout<<endl;
        copy(v4.begin(),v4.end(),ostream_iterator<float >(cout," "));cout<<endl;
        copy(v5.begin(),v5.end(),ostream_iterator<double>(cout," "));cout<<endl;
    }
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
//该程序的输出如下:
/*******************************************************************************
1 2 3
2 4 6 8
3 6 9 12 15
4.1 8.2 12.3 16.4 20.5 24.6
5.2 10.4 15.6 20.8 26 31.2 36.4
*******************************************************************************/
//data.txt中的内容如下:
/*******************************************************************************
1 2 3 *
2 4 6 8 *
3 6 9 12 15 *
4.1 8.2 12.3 16.4 20.5 24.6 *
5.2 10.4 15.6 20.8 26 31.2 36.4
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#endif//CODE2
#if 0

    很明显,CODE2中的代码段已经满足了我们的序列化要求。已经可以序列化各种不同类型
的数据,虽然CODE2中使用的是基本类型中的5种情况,但是由于C++的特色,对于类这样的扩
展类型完全可以象基本类型一样来对待,至于怎样才能够象基本类型一样来对待,这是C++基
础知识,基本上就是重载C++运算符。

    有人或许会说:上面的方法并不能够处理指针容器(容器中放置的是指针而不是对象)
实际上这是一种误导,虽然上面的代码中看不到指针的东西,但是STL中确确实实是采用的动
态内存分配的方式实现对象管理的,我们在使用STL的过程中实际上已经让STL替我们管理了
麻烦不断的内存指针问题!更确切点说,STL的游标概念就是泛化的指针。尽管如此,我们还
是要避免在容器中使用指针,虽然STL允许这么做,但是我并不提倡这一点,因为不用指针已
经可以很好的实现自己想要实现的任何功能了(当然这仅仅只是限于本文所介绍的序列化方
案)。对于其它的C++功能方面指针还是有它独到的能力,就我的使用经验来说,不用指针完
全可以实现指针所能够实现的所有功能,当然这里面包含了许许多多的C++高级知识,能够用
指针方便实现的当然还是用指针实现的好啊:)

    在这里作为提示,给出如果非要使用指针容器的情况如何实现序列化的方法:对指针容
器进行特殊处理,序列化到文件的时候一定要保证并不是把指针所指的对象序列化到了文件
而是将该指针在原来的对象容器中的索引保存到文件,从文件提取数据的时候同样是根据索
引值到对象容器中查找到对象并得到相应的指针放到指针容器中。从本段前面的讨论可以知
道:如果存在某个指针容器,就一定存在对应的对象容器,并且,在序列化输出和序列化输
入的时候都是先序列化的对象容器,然后才是指针容器,下面的CODE3中给出了具体的实例
代码:

#endif
#ifdef CODE3
////////////////////////////////////////////////////////////////////////////////
//模拟程序序列化的简单代码
#include <iostream>//cout
#include <fstream>//ofstream,ifstream
#include <vector>//vector
#include <iterator>//ostream_iterator,istream_iterator,back_inserter
#include <numeric>//partial_sum
#include <algorithm>//copy
#include <string>
#include <sstream>
#include <functional>
#include <map>
using namespace std;
//下面是针对输入和输出专门写的序列化模板
template<class Container>
ofstream& operator << (ofstream&out,Container&c)
{
    typedef typename Container::value_type T;
    copy(c.begin(),c.end(),ostream_iterator<T>(out," "));
    out << endl;
    return out;
}
template<class Container>
ifstream& operator >> (ifstream&in,Container&c)
{
    typedef typename Container::value_type T;
    string buffer;//每一行数据的临时缓冲区
    getline(in,buffer);//从文件中读取一行数据到缓冲区中
    istringstream isin(buffer);//构造输入字符串流
    copy(istream_iterator<T>(isin),istream_iterator<T>(),back_inserter(c));
    return in;
}
template<class Container,class PointerContainer>
ofstream& operator << (ofstream&out,pair<Container*,PointerContainer*> p)
{
    Container&c         = *(p.first);
    PointerContainer&cp = *(p.second);
    typename PointerContainer::iterator it = cp.begin();
    for(;it!=cp.end();++it)
    {
        typename Container::iterator itc;
        for(;itc!=c.end();++itc){
            if(&(*itc)==*it) out << distance(c.begin(),itc) << " " ;
        }
    }
    out << endl;
    return out;
}
template<class Container,class PointerContainer>
ifstream& operator >> (ifstream&in,pair<Container*,PointerContainer*> p)
{
    Container&c         = *(p.first);
    PointerContainer&cp = *(p.second);
    typedef typename Container::value_type T;
    string buffer;//每一行数据的临时缓冲区
    getline(in,buffer);//从文件中读取一行数据到缓冲区中
    istringstream isin(buffer);//构造输入字符串流
    size_t tmp;//用来临时保存读取的索引号
    while(isin >> tmp)//读取成功isin状态表示成功
    {
        typename Container::iterator it = c.begin();
        advance(it,tmp);//将游标移动到指定的索引位置
        cp.push_back(&(*it));//将这个对象的地址保存到地址数组中
    }
    return in;
}

int main()
{
    {//从程序序列化到文件
        vector<int> v(5,1);//[1,1,1,1,1]
        vector<int*> vp;//指针容器
        vp.push_back(&v[1]);
        vp.push_back(&v[3]);
        partial_sum(v.begin(),v.end(),v.begin());//[1,2,3,4,5]
        ofstream out("data.txt");//生成文件输出流
        //将数组v中的数据全部输出到文件流中,这种操作在C++中成为文件操作
        //在这里暂时称为序列化到文件操作。实际上这里为了简单序列化的格式
        //为文本文件格式。如果需要其它的格式完全可以通过自定义输出流游标
        //的方式,或者重载运算符operator<<和operator>>实现不同的序列化格
        //式。可以参见本人的其它相关文档。
        out << v << make_pair(&v,&vp) ;
    }
    {//从文件序列化到程序
        vector<int> v;//模拟应用程序中数据
        vector<int*> vp;//指针容器
        ifstream in("data.txt");//建立输入流
        //下面的这行代码从文件中提取数据到v中,模拟了应用程序的序列化过程
        in >> v >> make_pair(&v,&vp) ;
        //下面的这行代码仅仅只是为了显示是否真的被序列化到了程序中
        copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));
        cout << endl;
        //输出指针数组中指向的对象数据
        struct X{static void print(const int*ptr){cout<<*ptr<<" ";}};
        for_each(vp.begin(),vp.end(),X::print);
    }
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
//该程序的输出如下:
/*******************************************************************************
1 2 3 4 5
2 4
*******************************************************************************/
//data.txt中的内容如下:
/*******************************************************************************
1 2 3 4 5
1 3
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#endif//CODE3

### C++ 序列化与反序列化的概念 在C++中,序列化指的是将对象的状态转换成可以存储或传输的形式;而反序列化则是指从这种形式恢复对象的状态。这不仅有助于保存程序运行时的对象状态至文件系统或数据库,也方便在网络间传递复杂的数据结构[^1]。 ### 使用标准输入输出流操作符重载实现基本类型的序列化和反序列化 对于C++中的基础数据类型来说,可以通过重载`<<`(用于输出/写入)以及`>>`(用于输入/读取)这两个运算符来轻松完成它们之间的相互转化工作。这种方式使得自定义类也能像内置类型一样被处理[^2]。 ```cpp #include <iostream> #include <fstream> class Person { public: std::string name; int age; // Overload the << operator for serialization. friend std::ostream& operator<<(std::ostream &os, const Person &p) { os << p.name << ' ' << p.age; return os; } // Overload the >> operator for deserialization. friend std::istream& operator>>(std::istream &is, Person &p) { is >> p.name >> p.age; return is; } }; ``` ### 利用文件流进行实际的序列化和反序列化进程展示 当涉及到持久化存储或是跨进程通信的时候,则往往需要用到文件I/O来进行具体的编码解码动作。这里给出一段完整的代码片段作为例子说明: ```cpp int main() { // Serialization process Person person{"Alice", 30}; std::ofstream ofs("person.txt"); ofs << person; // Serialize object to file using overloaded << ofs.close(); // Deserialization process Person loaded_person{}; std::ifstream ifs("person.txt"); ifs >> loaded_person; // Deserialize from file into object using overloaded >> ifs.close(); } ``` 上述实例展示了如何利用C++的标准库函数配合上自定义的操作符重载机制,在不依赖第三方组件的情况下实现了简单的序列化及反序列化流程[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值