文章目录
1.引言
1.1 定义:
Google Protocol Buffer(简称 Protobuf)是一种轻便高效的结构化数据存储格式,平台无关、语言无关、可扩展,可用于通讯协议和数据存储等领域。
protocolbuf(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。
google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。
可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。
1.2 优点:
-
平台无关,语言无关,可扩展;
-
提供了友好的动态库,使用简单;
-
解析速度快,比对应的XML快约20-100倍;
-
序列化数据非常简洁、紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10。
1.3 编译安装:
-
apt安装依赖库: autoconf automake libtool curl make g++ unzip
-
git clone安装: https://github.com/google/protobuf
$ ./autogen.sh $ ./configure $ make $ make check $ sudo make install
2.Protobuf 使用
2.1 编写proto文件
首先需要一个proto文件,其中定义了我们程序中需要处理的结构化数据
// Filename: addressbook.proto// 这一行是注释,语法类似于C++
syntax="proto2";//表明使用protobuf的编译器版本为v2,目前最新的版本为v3
package addressbook;//声明了一个包名,用来防止不同的消息类型命名冲突,类似于 namespace
import "src/help.proto"; //举例用,编译时去掉
//导入了一个外部proto文件中的定义,类似于C++中的 include 。
//不过好像只能import当前目录及当前目录的子目录中的proto文件,
//比如import父目录中的文件时编译会报错(Import "../xxxx.proto" was not found or had errors.),使用绝对路径也不行因,
//官方文档说使用 -I=PATH 或者 --proto_path=PATH 来指定import目录,
//但实际实验结果表明这两种方式指定的是将要编译的proto文件所在的目录,而不是import的文件所在的目录。
// message 是Protobuf中的结构化数据,类似于C++中的类,可以在其中定义需要处理的数据
// required string name = 1; 声明了一个名为name,数据类型为string的required字段,字段的标识号为1
//protobuf一共有三个字段修饰符:
- required:该值是必须要设置的;
- optional :该字段可以有0个或1个值(不超过1个);
- repeated:该字段可以重复任意多次(包括0次),类似于C++中的list;
//string 是一种标量类型,protobuf的所有标量类型请参考文末的标量类型列表。
// name 是字段名,1 是字段的标识号,在消息定义中,每个字段都有唯一的一个数字标识号,
//这些标识号是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。标识号的范围在:1 ~ 229 - 1,其中[19000-19999]为Protobuf预留,不能使用。
// Person 内部声明了一个enum和一个message,这类似于C++中的类内声明,Person外部的结构可以用 Person.PhoneType 的方式来使用PhoneType。当使用外部package中的结构时,要使用 pkgName.msgName.typeName 的格式,每两层之间使用'.'来连接,类似C++中的"::"。
// optional PhoneType type = 2 [default = HOME]; 为type字段指定了一个默认值,当没有为type设值时,其值为HOME。
//另外,一个proto文件中可以声明多个message,在编译的时候他们会被编译成为不同的类。
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
message AddressBook {
repeated Person person_info = 1;
}
2.2 protoc编译proto文件
protoc是proto文件的编译器,目前可以将proto文件编译成C++、Java、Python三种代码文件,编译格式如下:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR /path/to/file.proto
上面的命令会生成addressbook.pb.h 和 addressbook.pb.cc两个C++文件。
2.3 调用addressbook.pb.h文件
现在编写一个main.cc文件调用pb文件
#include <iostream>
#include "addressbook.pb.h"
int main(int argc, const char* argv[])
{
addressbook::AddressBook person;
addressbook::Person* pi = person.add_person_info();
pi->set_name("aut");
pi->set_id(1219);
std::cout << "before clear(), id = " << pi->id() << std::endl;
pi->clear_id();
std::cout << "after clear(), id = " << pi->id() << std::endl;
pi->set_id(1087);
if (!pi->has_email())
pi->set_email("autyinjing@126.com");
addressbook::Person::PhoneNumber* pn = pi->add_phone();
pn->set_number("021-8888-8888");
pn = pi->add_phone();
pn->set_number("138-8888-8888");
pn->set_type(addressbook::Person::MOBILE);
uint32_t size = person.ByteSize();
unsigned char byteArray[size];
person.SerializeToArray(byteArray, size);
addressbook::AddressBook help_person;
help_person.ParseFromArray(byteArray, size);
addressbook::Person help_pi = help_person.person_info(0);
std::cout << "*****************************" << std::endl;
std::cout << "id: " << help_pi.id() << std::endl;
std::cout << "name: " << help_pi.name() << std::endl;
std::cout << "email: " << help_pi.email() << std::endl;
for (int i = 0; i < help_pi.phone_size(); ++i)
{
auto help_pn = help_pi.mutable_phone(i);
std::cout << "phone_type: " << help_pn->type() << std::endl;
std::cout << "phone_number: " << help_pn->number() << std::endl;
}
std::cout << "*****************************" << std::endl;
return 0;
}
参考:https://blog.csdn.net/DinnerHowe/article/details/79805153