博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Google Protocol Buffers浅析(三)
阅读量:4545 次
发布时间:2019-06-08

本文共 4097 字,大约阅读时间需要 13 分钟。

     本文主要会介绍怎么使用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等方式都应该比较类似,所以感兴趣的朋友可以自己动手试试。   

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

        欢迎转载,转载时请务必保留原文出处: ,谢谢合作! 

转载于:https://www.cnblogs.com/royenhome/archive/2010/10/30/1865153.html

你可能感兴趣的文章
2018年11月14日 学习字符串用法2
查看>>
2019年5月26日 re模块2
查看>>
Mac显示器不亮
查看>>
luogu P2312 解方程
查看>>
Cordova开发速记
查看>>
Chrome开发工具
查看>>
MySQL 的 RowNum 实现
查看>>
网络工程师应该掌握的44个路由器问题
查看>>
windows 控制台下运行cl命令
查看>>
(七十八)使用第三方框架INTULocationManager实现定位
查看>>
LeetCode问题:搜索插入位置
查看>>
JVM基础学习之基本概念、可见性与同步
查看>>
UML入门
查看>>
CodeForces - 524F And Yet Another Bracket Sequence
查看>>
python学习笔记-day10-2【多进程,多线程】
查看>>
MySQL安装后的初始优化
查看>>
PHP记录商品历史纪录
查看>>
类型转换 盲区
查看>>
Android Studio does not point to a valid jvm
查看>>
第5月第13天 node cnpm安装 babel
查看>>