Google Protocol Buffers浅析(三)

来源:互联网 发布:新手 基金 知乎 编辑:程序博客网 时间:2024/05/21 15:00
本文主要会介绍怎么使用Google Protocol的Lib来序列化我们的数据,方法很多种,本文只介绍其中的三种,其他的方法读者可以通过自行研究摸索。但总的来说,序列化数据总的来说分为以下俩步:

1)使用数据源填充数据结构,无论数据源来自文件还是内存还是标准输入

2)利用Lib提供的序列化接口将数据结构序列化,然后存储在内存或者磁盘上

一、填充数据结构

从数据源中获取数据,这儿的数据源可能来自磁盘上的一个文件或者内存中存储的一段数据或者来自标准输入的数据。我们需要做的就是,将AddressBook这个数据结构中的各个字段填充。本例中是通过AddressBook提供的add_person函数来获得一个Person的指针,从而对其进行填充,如下代码所示:

//地址簿数据定义
AddressBook addressBook;

//第一个联系人的数据定义与初始化
Person *personMe= addressBook.add_person();
personMe
->set_id(1);
personMe
->set_name("royen");
personMe
->set_email("zwg19891129@163.com");
personMe
->set_unsure("19bf173a0e87ab");

//第二个联系人的数据定义与初始化
Person *personHim= addressBook.add_person();
personHim
->set_id(2);
personHim
->set_name("XXX");
personHim
->set_email("XXX@XXX.com");
personHim
->set_unsure("19bf173a0e87ab");

//personMe的手机号码数据定义与初始化
Person_PhoneNumber *phoneNumberMobile= personMe->add_phone();
phoneNumberMobile
->set_number("15996110120");
phoneNumberMobile
->set_type(Person_PhoneType_MOBILE);

//personMe的座机号码数据定义与初始化
Person_PhoneNumber *phoneNumberHome= personMe->add_phone();
phoneNumberHome
->set_number("0256110120");
phoneNumberHome
->set_type(Person_PhoneType_HOME);

//personHim的一个号码数据定义与初始化
Person_PhoneNumber *phoneNumberHim= personHim->add_phone();
phoneNumberHim
->set_number("15996111111");
phoneNumberHim
->set_type(Person_PhoneType_HOME);
复制代码

很容易看出,上述代码即在地址簿中添加了俩个联系人,然后又分别填充各个联系人的数据信息,通过上述代码一个地址簿的数据便准备好了。

二、序列化数据

其实通过看编译器生成的AddressBook这个类所提供的方法名,既可以大致知道有哪些序列化的方式,如下所示:

从上图可以看出,可利用序列化的方法很多,本文中主要使用SerializeToString、SerializeToCodedStream以及SerializeToOstream来完成序列化。

下面就分别就这几种方式来介绍下:

1) SerializeToCodedStream方式

首先可以知道该函数的原型是bool SerializeToCodedStream(std::ostream *),所以使用该函数需要结合C++的fstream流,代码如下:

//方法一: 使用SerializePartialToOstream来序列化,注意ios::binary以二进制流写入文件
fstream fserial("addressbook.data",ios::out| ios::trunc| ios::binary);
if (!addressBook.SerializePartialToOstream(&fserial))
{
cerr
<<"Failed to serial address book data!\n";
return;
}
cout
<<"Serial address book data successfully!\n";
fserial.close();
fserial.clear();
复制代码

可以看出,采用这种方法相当的便捷,而且也很简洁,但有个缺点就是输出到文件的编码格式不好控制,所以可以使用下面介绍的这种方法。

2)SerializeToString方式

函数原型为bool SerializeToString(std::string* output) ,所以可以讲填充在数据结构AddressBook中的数据取出存到一个string对象中,然后再以二进制流的方式将其写入到磁盘文件中,代码如下:

FILE *g_AddressBook= fopen("addressbook.data","wb,ccs = UNICODE");
if( NULL == g_AddressBook )
{
cerr
<<"Create addressbook.data failed!\n";
return ;
}

string serialStream= "";
if( !addressBook.SerializePartialToString(&serialStream) )
{
cerr
<<"Failed to serial addressbook data!\n";
return;
}

fwrite( serialStream.c_str(),
sizeof(char),addressBook.ByteSize(),g_AddressBook);
cout
<<"serial address successfully!\n";
if( g_AddressBook )
{
fclose(g_AddressBook);
g_AddressBook
= NULL;
}
复制代码

上述代码稍微繁琐了点,但是也是一种序列化的方式,通过结合使用C库中的文件操作函数,可以更方便的定制输出文件。

3)SerializeToCodedStream方式

该方式主要指用到的google buffer的库中提供的一组数据流操作对象,在使用这些对象之前需要引入一些头文件,如下所示:

#include <google/protobuf/io/zero_copy_stream_impl.h>
#include
<google/protobuf/io/zero_copy_stream.h>
#include
<google/protobuf/io/coded_stream.h>
using namespace::google::protobuf::io;
复制代码

该方式也结合C库的open与write函数,序列化部分的代码如下:

int fd= _open("addressbook.data", _O_WRONLY|_O_CREAT| _O_BINARY, _S_IREAD|_S_IWRITE);
if( -1== fd )
{
cerr
<<"Create addressbook.data failed!\n";
return ;
}
char tmpArr[MAX_SIZE];
memset(tmpArr,
0,sizeof(tmpArr));
ZeroCopyOutputStream
*raw_output= new ArrayOutputStream(tmpArr,addressBook.ByteSize()+1);
CodedOutputStream
* coded_output= new CodedOutputStream(raw_output);
if( !addressBook.SerializeToCodedStream( coded_output ))
{
cerr
<<"Fail to serial addressbook data!\n";
return;
}
_write(fd,tmpArr,addressBook.ByteSize()
+1);
cout
<<"serial address successfully!\n";
delete coded_output;
delete raw_output;
close(fd);
复制代码

本文暂时介绍这三种序列化话方式,还有像SerializeToArray以及SerializeToFileDescriptor等方式都应该比较类似,所以感兴趣的朋友可以自己动手试试。

下篇文章再稍微介绍下反序列化的方法,但是应该不会太多内容,毕竟都方法都很相似。

欢迎转载,转载时请务必保留原文出处:http://www.cnblogs.com/royenhome ,谢谢合作!