Protobuf 概述
- protobuf也叫protocol buffer是google 的一种
数据交换的格式
,它独立于语言,独立于平台
。google 提供了多种语言的实现:java、c#、c++
、go 和 python 等,每一种实现都包含了相应语言的编译器以及库文件。 - 由于它是一种
二进制的格式
,比使用 xml 、json进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。 - 此外相较于json和xml,protobuf还支持复杂的数据格式,如嵌套的结构体,树,图,等。
如何使用
step1
创建一个新的文件, 文件名随意指定, 文件后缀为 .proto
根据protobuf
的语法, 编辑.proto
文件,
syntax="proto3";
message 名字
{
// 类中的成员, 格式
数据类型 成员名字 = 1;
数据类型 成员名字 = 2;
数据类型 成员名字 = 3;
......
......
}
根据我们想要的数据格式指定messge的格式,protobuf中的格式语法与c++结构体大致相同,这里我们假定编辑一个person的结构体
syntax = "proto3";
// 在该文件中对要序列化的结构体进行描述
message Person
{
int32 id = 1;
bytes name = 2;
bytes sex = 3;
int32 age = 4;
}
step2
.proto
文件编辑好之后就可以使用protoc
工具将其转换为C++文件了,执行之后就会得到xxx.pb.h,与xxx.pb.c
$ protoc -I path .proto文件 --cpp_out=输出路径(存储生成的c++文件)
- 在
protoc
命令中,-I
参数后面可以跟随一个或多个路径,用于告诉编译器在哪些路径下查找导入的文件或依赖的文件,使用绝对路径或相对路径都是没问题的。
step3
需要使用的地方的#include"xxx.pb.h"
序列化与反序列化
#include "MyTest.h"
#include "Person.pb.h"
void MyTest::test() {
Person p;
p.set_id(10);
p.set_age(32);
p.set_sex("man");
p.set_name("lucy");
std::string output;
p.SerializeToString(&output);//序列化 并且output是二进制而不是字符串
Person pp;
pp.ParseFromString(output);//反序列化
std::cout<<pp.id()<<","<<pp.sex()<<","<<pp.name()<<","<<pp.age()<<","<<std::endl;
}
messge中嵌套messge
与c++中结构体一样 写在一个文件中就行
message Address
{
int32 num=1;
bytes addr = 2;
}
message Person
{
int32 id = 1;
bytes name = 2;
bytes sex = 3;
int32 age = 4;
Address addr=5;
}
需要注意:在进行序列化时 person.addr是一个const值不可修改 这里需要使用mutable_addr(得到一个地址)
person.mutable_addr()->set_addr("武汉市")
person.mutable_addr()->set_num(1)
messge中嵌套数组
使用repeat关键字(动态数组)
message Person
{
int32 id = 1;
repeat bytes name = 2;
bytes sex = 3;
int32 age = 4;
Address addr=5;
}
初始化
//方式一
person.add_name();//分配内存
person.set_name(index,value);
//方式二
person.add_name(value);
messge中嵌套枚举
与c++中枚举不同的是 第一个元素必须指定为0
,元素之间用;
隔开
// 定义枚举类型
enum Color
{
Red = 0;
Green = 3; // 第一个元素以外的元素值可以随意指定
Yellow = 6;
Blue = 9;
}
// 在该文件中对要序列化的结构体进行描述
message Person
{
int32 id = 1;
repeated bytes name = 2;
bytes sex = 3;
int32 age = 4;
Color color = 5; // 枚举类型
}
初始化
person.set_color(Color::blue)
导入protoc文件
Address.proto
syntax = "proto3";
message Address
{
int32 num=1;
bytes addr = 2;
}
Person.proto
syntax = "proto3";
import "Address.proto"
message Person
{
int32 id = 1;
repeat bytes name = 2;
bytes sex = 3;
int32 age = 4;
Address addr=5;
}
执行命令
因为有两个proto文件所以这里需要执行两个命令
protoc Person.proto --cpp_out=./
protoc Address.proto --cpp_out=./
同样也会得到两个文件 Person.pb.h 与 Address.pb.h 两个文件都有include到使用的文件
包
在C++中通过namespace来进行避免重名和分类,在protobuf中则是包(package)
proto文件 - Address.proto
syntax = "proto3";
// 添加命名空间 Dabing
package Dabing;
// 地址信息, 这个Address类属于命名空间: Dabing
message Address
{
bytes addr = 1;
bytes number = 2;
}
proto文件 - Person.proto
syntax = "proto3";
// 使用另外一个proto文件中的数类型, 需要导入这个文件
import "Address.proto";
// 指定命名空间 ErBing
package ErBing;
// 以下的类 Person 和枚举 Color 都属于命名空间 ErBing
// 在该文件中对要序列化的结构体进行描述
// 定义枚举类型
enum Color
{
Red = 0;
Green = 3; // 第一个元素以外的元素值可以随意指定
Yellow = 6;
Blue = 9;
}
// 在该文件中对要序列化的结构体进行描述
message Person
{
int32 id = 1;
repeated bytes name = 2;
bytes sex = 3;
int32 age = 4;
// 枚举类型
Color color = 5;
// 添加地址信息, 使用的是外部proto文件中定义的数据类型
// 如果这个外边类型属于某个命名空间, 语法格式:
// 命名空间的名字.类名 变量名=编号;
Dabing.Address addr = 6;
}
在c++中使用
//方式一
using namespace Dabing
using namespace Erbing
//方式二
Erbing::Person p;