C++序列化与反序列化的简单探索

序列化是指将数据从有结构清晰的语言定义的数据形式转化为二进制字符串,反序列化则是序列化的逆操作。

百度百科定义序列化如下:

序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。


关于序列化,其实在MapBox中就已经有雏形。MapBox的作法是使用map<string,string>作为底层容器,第一元素为 Tag标签,第二元素为 Context内容。方法toString()能够将MapBox转化为一个字符串,这个过程中MapBox是使用base64编码+特殊字符实现的。MapBox没有提供反序列化方法,只是在通过网络接收数据的时候能够 将字符串解析为MapBox。(毕竟MapBox的设计初衷就是网络编程,起初打算打造一个MapBox over socket的东西...,但是发现base64本身存在的缺点在数据量暴涨的时候大于base64的优点,于是放弃了MapBox over socket的设计。但是现在MapBox在传输不超过200MB的内容时仍然具有较好的表现)

最近翻了翻MapBox,序列化与反序列化的想法又冒了出来。Protocol Buffer的话,需要调用谷歌自己的编译程序(把rule编译为header),感觉比较麻烦(不过比其他的工具简单不少了)。Boost库的话,序列化倒是很方便,但是看了源码之后发现各种模板暂时理解不能... 所以还是想自己写一个小东西试一试。

于是就有了这么个东西 ( View Source On GitHub )

/** Templates */
template<typename T>
string serialize(const T& a)
{
    return T::serialize(a);
}

template<typename T>
int deserialize(string str,T& a)
{
    return T::deserialize(str,a);
}
这里是序列化以及反序列化的通用方法。其实就是调用了一下T类型的序列化和反序列化函数。


/** Special Version
* For...
*   int, double, float
*/
/// int
template<>
string serialize(const int& a)
{
    string ans;
    int c=htonl(a);
    ans.append((const char*)&c,sizeof(c));
    return ans;
}
template<>
int deserialize(string str,int& c)
{
    memcpy(&c,str.data(),sizeof(c));
    c=ntohl(c);
    return sizeof(c);
}
上面是针对int的特化,进行了字节序的转换


/// string
template<>
string serialize(const string& a)
{
    int len=a.size();
    string ans;
    ans.append(::serialize(len));
    ans.append(a);
    return ans;
}
template<>
int deserialize(string str,string& a)
{
    int len;
    ::deserialize(str,len);
    a=str.substr(sizeof(len),len);
    return sizeof(int)+len;
}
以上是针对string的特化,在序列化最前面加入了长度。之前没有这一个长度,结果string和string相连的时候出现了比较棘手的问题......


/// Marco definition
#define NORMAL_DATA_SERIALIZE(Type) template<> \
    string serialize(const Type& a) \
    { \
        string ans; \
        ans.append((const char*)&a,sizeof(a)); \
        return ans; \
    }
#define NORMAL_DATA_DESERIALIZE(Type) template<> \
    int deserialize(string str,Type& a)\
    { \
        memcpy(&a,str.data(),sizeof(a)); \
        return sizeof(a); \
    }

针对POD而且不需要字节序转换的宏定义


/// double
NORMAL_DATA_SERIALIZE(double);
NORMAL_DATA_DESERIALIZE(double);
NORMAL_DATA_SERIALIZE(float);
NORMAL_DATA_DESERIALIZE(float);
NORMAL_DATA_SERIALIZE(char);
NORMAL_DATA_DESERIALIZE(char); 
double与char,视为POD+无字节序问题的类型


template<typename SerializableType>
class Serializable
{
public:
    static SerializableType deserialize(string);
    static string serialize(const SerializableType& a);
};
可序列化模板类,如果某个类型希望能够序列化,那么应该继承这个类并实现其中的两个方法。


class OutEngine
{
public:
    template<typename SerializableType>
    OutEngine& operator << (SerializableType& a)
    {
        string x=::serialize(a);
        os.write(x.data(),x.size());
        return *this;
    }
    string str()
    {
        return os.str();
    }
    void set_empty()
    {
        os.str("");
    }
    OutEngine():os(std::ios::binary){}
public:
    ostringstream os;
};
一个输出引擎,使用方法大概就是声明 OutEngine oe;int a=3;然后oe<<a; 调用oe.str()来获取序列化字串


class InEngine
{
public:
    InEngine(string s) : is(s){n_size=leftsize();}
    template<typename SerializableType>
    InEngine& operator >> (SerializableType& a)
    {
        int ret=::deserialize(is,a);
        is=is.substr(ret);
        return *this;
    }
    void set_str(string s)
    {
        is=s;
        n_size=leftsize();
    }

    int leftsize()
    {
        return is.size();
    }
    int donesize()
    {
        return n_size-leftsize();
    }

protected:
    string is;
    int n_size;
};
一个输入引擎,原来使用istringstream,后来就直接换成了string,配合substr


测试代码如下

注: 三个成员设计为public是因为方便main里面的代码

class cbox : public Serializable<cbox>
{
public:
    int a;
    double b;
    string str;

    static string serialize(const cbox& inc)
    {
        OutEngine x;
        x<<inc.a<<inc.b<<inc.str;
        return x.str();
    }
    static int deserialize(string inc,cbox& box)
    {
        InEngine x(inc);
        x>>box.a>>box.b>>box.str;
        return x.donesize();
    }
};
int main()
{
    cbox box;
    box.a=11;
    box.b=6.6;
    box.str="Hello World";

    cbox box3;
    box3.a=33;
    box3.b=12.5;
    box3.str="Yummy Hamburger!";

    OutEngine oe;
    oe<<box<<box3;

    string b=oe.str();
    cout<<b<<endl;

    cbox box2;
    cbox box4;
    InEngine ie(b);
    ie>>box2>>box4;

    cout<<box2.a<<endl;
    cout<<box2.b<<endl;
    cout<<box2.str<<endl;

    cout<<box4.a<<endl;
    cout<<box4.b<<endl;
    cout<<box4.str<<endl;
    return 0;
}

输出内容(二进制字串在不同设备上的输出应该是不同的,但是内容应该是一致的)



  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
序列化是将对象转换成字节流的过程,而反序列化是将字节流转换回原始对象的过程。序列化反序列化在网络传输和存储数据中起到了重要的作用。 序列化的优点在于: 1. 数据传输:序列化后的字节流可以方便地在网络中传输,可以减少网络传输的时间和带宽消耗。 2. 数据存储:序列化后的字节流可以被持久化保存在磁盘中,方便后续读取和使用。 3. 跨平台交互:序列化后的字节流可以在不同的编程语言和操作系统间进行交互,实现跨平台的数据传输和共享。 然而,序列化也存在一些缺点,比如: 1. 复杂性:为每种对象类型定义专门的序列化反序列化方法相对复杂,需要额外的工作量和代码维护。 2. 可读性:序列化后的字节流通常是机器可读的,不易阅读和理解。 在实际应用中,根据具体需求和场景,可以选择不同的序列化方法。比如,在引用中提到了JSON序列化和专用序列化方法,JSON序列化具有较好的可读性,但序列化后占用的字节较多;而专用序列化方法虽然序列化后字节较少,但需要为每种对象类型定义专门的序列化反序列化方法。 总结起来,序列化是将对象转换成字节流的过程,而反序列化是将字节流转换回原始对象的过程。序列化的优点包括方便的数据传输和存储,以及跨平台交互的能力。然而,序列化也存在复杂性和可读性的问题。在实际应用中,需要根据需求选择合适的序列化方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值