目录
前言:
提示:
插件
入门:
ProtoBuf普通字段类型:
编译命令:
序列化与反序列化:
Proto3语法:
字段规则:数组类型
应用
将通讯录数据序列化后写入文件
工具介绍:hexdump
将文件数据反序列化到类中:
ProtoBuf命令选项:
Decode选项:
Proto3语法:
字段规则:enum类型
应用:
Proto3语法:
字段规则:Any类型:
any的相关接口介绍:
应用:
Proto3语法:
字段规则:oneof类型:
应用:
Proto3语法:
字段规则:map类型:
应用:
Proto3语法:
字段规则:默认值:
Proto3语法:
字段规则:更新消息:
字段规则:reserved:
Proto3语法:
字段规则:option:
前言:
提示:
安装等过程在此处就不阐述,请读者自行查阅:
代码演示环境:ubuntu22.04云服务器
插件
vscode支持ProtoBuf语法高亮的插件:vscode-proto
入门:
1.首先创建一个.proto文件
2.声明语法指定行
syntax = "proto3";
3.声明命名空间
package contacts;
4.定义一个message消息
message PeopleInfo
{string _name = 1;int32 _age = 2;
}
//其中,1和2都是ProtoBuf用来标识字段唯一性的ID,同一个message中id不能相同也不能缺省;
//ID范围 = {1,2^29-1],其中19000~19999之间的数不可用;
ProtoBuf普通字段类型:
//ProtoBuf类型
double
double
float
float
int32 :使⽤变⻓编码[1]。负数的编码效率较低⸺若字段可能为负值,应使⽤ sint32 代替。
int64 :使⽤变⻓编码[1]。负数的编码效率较低⸺若字段可能为负值,应使⽤ sint64 代替。
uint32:使⽤变⻓编码[1]。
uint64 :使⽤变⻓编码[1]。
sint32 :使⽤变⻓编码[1]。符号整型。负值的编码效率⾼于常规的 int32 类型。
sint64 :使⽤变⻓编码[1]。符号整型。负值的编码效率⾼于常规的 int64 类型。
fixed32: 定⻓ 4 字节。若值常⼤于2^28 则会⽐ uint32 更⾼效。
fixed64 :定⻓ 8 字节。若值常⼤于2^56 则会⽐ uint64 更⾼效。
sfixed32:定⻓ 4 字节。
sfixed64:定⻓ 8 字节。
bool类型
string:包含 UTF-8 和 ASCII 编码的字符串,⻓度不能超过 2^32 。
bytes:可包含任意的字节序列但⻓度不能超过 2^32 。
编译命令:
1.编译当前路径下的.protoc文件protoc --cpp_out=要保存的路径名 依赖的.proto文件例如:protoc --cpp_out=. contact.proto
2.编译指定路径下的.protoc文件protoc -I .protoc文件所处路径 --cpp_out=要保存的路径名 依赖的.proto文件
序列化与反序列化:
注意:ProtoBuf序列化后不是文本序列,而是二进制序列!
序列化接口:以Serialize开头
反序列化接口:以Parse开头
#include <iostream>
#include <string>
//#include"ProtoBuf编译后的头文件.h"
#include "contacts.pb.h"int main()
{std::string data;//序列化{contacts::PeopleInfo ple;ple.set_name("张三");ple.set_age(20);// 序列化if (!ple.SerializeToString(&data)){std::cout << "序列化联系人信息失败!" << std::endl;return -1;}std::cout << "Serialized str = " << data << std::endl;}//反序列化{contacts::PeopleInfo ple;if(!ple.ParseFromString(data)){std::cout<<"反序列化失败!"<<std::endl;return -2;}std::cout<<"反序列化成功!"<<std::endl<<"姓名: "<<ple.name()<<std::endl<<"年龄:"<<ple.age()<<std::endl;}return 0;
}
Makefile文件:
Main:Main.cpp contacts.pb.ccg++ -o $@ $^ -std=c++11 -lprotobuf
.PHONY:clean
clean:rm -f Main
Proto3语法:
字段规则:数组类型
1.数组类型:repeated
//样例1:
message PeopleInfo
{string name = 1;int32 age = 2;repeated string phone_numbers = 3;
}
//也可以是message类型的数组
message PhoneNumbers
{string phone = 1;
}
message PeopleInfo
{string name = 1;int32 age = 2;repeated PhoneNumbers phones = 3;
}
在一个.proto文件中使用另外一个.proto文件的message:
//以phone.proto和contacts.proto为例
//phone.proto
message PhoneNumbers
{string phone = 1;
}
//contacts.proto
syntax = "proto3";
package contacts;
import "phone.proto";message PeopleInfo
{string name = 1;int32 age = 2;repeated phone.PhoneNumbers phones = 3;
}
/*注意,如果外部的.proto文件中存在命名空间,则在本文件inport引入后,需要指定空间(空间名+.)
才能使用message*/
应用
将通讯录数据序列化后写入文件
接口介绍:以上述.proto文件编译后的文件为例:
1.add_contacts()
作用:返回一块新的contacts数组中的空间地址用以用户填充相关字段,作用类似于push_back
2.add_phones()
作用:返回一块新的contacts数组中phone数组中的空间地址用以用户填充相关字段,作用类似于push_back
3.cin.ignore()
作用:cin的ignore函数可以用来清空cin的缓冲区,传递一个足够大的参数和一个终止符即可,cin会根据传递的大小和终止符来清空包括终止符在内的缓冲区数据
code:
#include <iostream>
#include <fstream>
#include <string>
#include "contacts.pb.h"void AddContact(contacts::PeopleInfo *con)
{std::cout<<"---------------新增联系人---------------"<<std::endl;std::cout<<"输入联系人姓名: ";std::string name;std::getline(std::cin, name);con->set_name(name);int age = 0;std::cout<<"输入联系人年龄: ";std::cin >> age;con->set_age(age);// 清空cin缓冲区std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');// 新增电话for(int i = 1;true;i++){std::cout<<"---请输入该联系人的电话"<<i<<"(只输入空格表示完成)"<<": ";std::string number;std::getline(std::cin,number);if(number.size() == 0) break;contacts::PeopleInfo_Phone* phone = con->add_phones();phone->set_phone(number);}std::cout<<"---------------添加联系人成功---------------"<<std::endl;
}
int main()
{contacts::Contacts cons;std::ifstream in("contacts.bin", std::ios::binary);if (!in.is_open()){std::cout << "contacts.bin is not exist,create new contacts.bin" << std::endl;}else if (!cons.ParseFromIstream(&in)){std::cout << "Parse contact info error!" << std::endl;in.close();return -1;}AddContact(cons.add_contacts());// 序列化写入文件std::ofstream out("contacts.bin", std::ios::trunc | std::ios::binary);if (!out.is_open()){std::cout << "write file error!" << std::endl;return -2;}if (!cons.SerializePartialToOstream(&out)){std::cout << "序列化失败!" << std::endl;return -3;}in.close();out.close();return 0;
}
工具介绍:hexdump
将二进制数据转换为十六进制:
指令:hexdump -C 二进制文件
将文件数据反序列化到类中:
#include <iostream>
#include <fstream>
#include "contacts.pb.h"int main()
{contacts::Contacts cons;std::ifstream in("contacts.bin", std::ios::in | std::ios::binary);if (!in.is_open()){std::cout << "open contacts.bin error!" << std::endl;return -1;}else if (!cons.ParsePartialFromIstream(&in)){std::cout << "反序列化失败!" << std::endl;return -1;}for(int i = 0;i<cons.contacts_size();i++){std::cout<<"-------------------------------"<<std::endl;std::cout<<"------------联系人-------------"<<std::endl;auto people = cons.contacts(i);std::cout<<"姓名: ";std::cout<<people.name()<<std::endl;std::cout<<"年龄: "<<people.age()<<std::endl;std::cout<<"-----------联系电话-------------"<<std::endl;for(int i = 0;i<people.phones_size();i++){std::cout<<(people.phones(i)).phone()<<std::endl;}std::cout<<"----------- 联系人-------------"<<std::endl;std::cout<<"-----------------------------"<<std::endl;}in.close();return 0;
}
ProtoBuf命令选项:
查询ProtoBuf指令手册指令:protoc -h
Decode选项:
从标准输入流中将二进制数据转换成文本类型数据,可以将stdin重定向到某个二进制类型文件:
protoc --decode=命名空间.指定的message类型的变量名 指定的.proto文件 < 指定的二进制文件
Proto3语法:
字段规则:enum类型
枚举类型中,第一个枚举的变量值一定要以0开头,枚举类型中的变量不能设置为负值,且只能是32位数字:
注意:1.同一个文件中的枚举类型中的变量名不能相同;
2.在多个.proto文件中,如果枚举类型名字相同,需要申明一下package
3.import一个.proto文件后,如果不适用其中的任何东西,编译时会有warning
syntax = "proto3";
package contacts;message PeopleInfo
{string name = 1;int32 age = 2;message Phone {string number = 1;enum PhoneType{HP = 0;TEL = 1;}PhoneType type = 2;}repeated Phone phones = 3;
}message Contacts
{repeated PeopleInfo contacts = 1;
}
应用:
接口介绍:1.set_type(type)作用:设置类型,在上述例子中参数为:contacts::PeopleInfo_Phone_PhoneType中的常量2.PhoneType_Name(type)作用:将传递的参数type转换成字符串返回3.Phones(int index)作用:返回数组中index下标对应的元素
//write.cc
#include <iostream>
#include <fstream>
#include <string>
#include "contacts.pb.h"void AddContact(contacts::PeopleInfo *con)
{std::cout<<"---------------新增联系人---------------"<<std::endl;std::cout<<"输入联系人姓名: ";std::string name;std::getline(std::cin, name);con->set_name(name);int age = 0;std::cout<<"输入联系人年龄: ";std::cin >> age;con->set_age(age);// 清空cin缓冲区std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');// 新增电话for(int i = 1;true;i++){std::cout<<"---请输入该联系人的电话"<<i<<"(只输入空格表示完成)"<<": ";std::string number;std::getline(std::cin,number);if(number.size() == 0) break;contacts::PeopleInfo_Phone* phone = con->add_phones();// phone->set_phone(number);phone->set_number(number);std::cout<<"--请输入该联系人电话类型:|1.HP--2.TEL|-----: ";int type;std::cin>>type;switch(type){case 1: phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_HP);break;case 2:phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL);break;}}std::cout<<"---------------添加联系人成功---------------"<<std::endl;
}
int main()
{contacts::Contacts cons;std::ifstream in("contacts.bin", std::ios::binary);if (!in.is_open()){std::cout << "contacts.bin is not exist,create new contacts.bin" << std::endl;}else if (!cons.ParseFromIstream(&in)){std::cout << "Parse contact info error!" << std::endl;in.close();return -1;}AddContact(cons.add_contacts());// 序列化写入文件std::ofstream out("contacts.bin", std::ios::trunc | std::ios::binary);if (!out.is_open()){std::cout << "write file error!" << std::endl;return -2;}if (!cons.SerializePartialToOstream(&out)){std::cout << "序列化失败!" << std::endl;return -3;}in.close();out.close();return 0;
}
//read.cc
#include <iostream>
#include <fstream>
#include "contacts.pb.h"int main()
{contacts::Contacts cons;std::ifstream in("contacts.bin", std::ios::in | std::ios::binary);if (!in.is_open()){std::cout << "open contacts.bin error!" << std::endl;return -1;}else if (!cons.ParsePartialFromIstream(&in)){std::cout << "反序列化失败!" << std::endl;return -1;}for(int i = 0;i<cons.contacts_size();i++){std::cout<<"-------------------------------"<<std::endl;std::cout<<"------------联系人-------------"<<std::endl;auto people = cons.contacts(i);std::cout<<"姓名: ";std::cout<<people.name()<<std::endl;std::cout<<"年龄: "<<people.age()<<std::endl;std::cout<<"-----------联系电话-------------"<<std::endl;for(int i = 0;i<people.phones_size();i++){contacts::PeopleInfo_Phone phone = people.phones(i);std::cout<<phone.number()<<std::endl;std::cout<<"电话类型: "<<phone.PhoneType_Name(phone.type())<<std::endl;}std::cout<<"----------- 联系人-------------"<<std::endl;std::cout<<"-----------------------------"<<std::endl;}in.close();return 0;
}
Proto3语法:
字段规则:Any类型:
首先,先查看ProtoBuf库的路径位置:
/usr/local/include/google/protobuf/
在这个路径下存在一个any.ptoto文件
//1,引入any.proto
import "google/protobuf/any.protoc";
syntax = "proto3";
package contacts;import "google/protobuf/any.proto";//地址信息
message Address{string util_address = 1;string home_address = 2;
}message PeopleInfo{string name = 1;int32 age = 2;message Phone {string number = 1;enum PhoneType{MP = 0;TEL = 1;}PhoneType type = 2;}repeated Phone phones = 3;google.protobuf.Any data = 4;
}message Contacts{repeated PeopleInfo contacts = 1;
}
any的相关接口介绍:
any在google::protobuf::Any::中,即定义:google::protobuf::Any::变量名
1.mutablek开头的接口()
作用:返回一个开辟好的any空间
在上述.proto文件编译的前提下:接口名为:mutablek_data();
2.PackFrom(message&)
作用:将一个message类型的对象存储到any对象中
3.UnpackTo(message*)
作用:将一个any对象中存储的message对象提取出来;
4.has_data()
作用:判断内部的any对象是是否有数据,有则返回true.反之false
应用:
//为联系人信息增加地址信息
//write.cc
#include <iostream>
#include <fstream>
#include <string>
#include "contacts.pb.h"void AddContact(contacts::PeopleInfo *con)
{std::cout<<"---------------新增联系人---------------"<<std::endl;std::cout<<"输入联系人姓名: ";std::string name;std::getline(std::cin, name);con->set_name(name);int age = 0;std::cout<<"输入联系人年龄: ";std::cin >> age;con->set_age(age);// 清空cin缓冲区std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');// 新增电话for(int i = 1;true;i++){std::cout<<"---请输入该联系人的电话"<<i<<"(只输入空格表示完成)"<<": ";std::string number;std::getline(std::cin,number);if(number.size() == 0) break;contacts::PeopleInfo_Phone* phone = con->add_phones();// phone->set_phone(number);phone->set_number(number);std::cout<<"--请输入该联系人电话类型:|1.移动电话--2.固定电话|-----: ";int type;std::cin>>type;std::cin.ignore(256,'\n');switch(type){case 1: phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP);break;case 2:phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL);break;}//填充地址信息contacts::Address address;std::cout<<"请输入工作单位地址(不填请按空格结束): ";std::string util_address;std::getline(std::cin,util_address);address.set_util_address(util_address);std::string home_address;std::cout<<"请输入家庭地址(不填请按空格结束): ";std::getline(std::cin,home_address);address.set_home_address(home_address);//people中的mutable_data方法会返回一个开辟好的any对象地址google::protobuf::Any* any = con->mutable_data();//将message信息解析到any中any->PackFrom(address);}std::cout<<"---------------添加联系人成功---------------"<<std::endl;
}
int main()
{contacts::Contacts cons;std::ifstream in("contacts.bin", std::ios::binary);if (!in.is_open()){std::cout << "contacts.bin is not exist,create new contacts.bin" << std::endl;}else if (!cons.ParseFromIstream(&in)){std::cout << "Parse contact info error!" << std::endl;in.close();return -1;}AddContact(cons.add_contacts());// 序列化写入文件std::ofstream out("contacts.bin", std::ios::trunc | std::ios::binary);if (!out.is_open()){std::cout << "write file error!" << std::endl;return -2;}if (!cons.SerializePartialToOstream(&out)){std::cout << "序列化失败!" << std::endl;return -3;}in.close();out.close();return 0;
}
//read.cc
#include <iostream>
#include <fstream>
#include "contacts.pb.h"int main()
{contacts::Contacts cons;std::ifstream in("contacts.bin", std::ios::in | std::ios::binary);if (!in.is_open()){std::cout << "open contacts.bin error!" << std::endl;return -1;}else if (!cons.ParsePartialFromIstream(&in)){std::cout << "反序列化失败!" << std::endl;return -1;}for (int i = 0; i < cons.contacts_size(); i++){std::cout << "------------联系人--------------" << std::endl;std::cout << "-------------------------------" << std::endl;auto people = cons.contacts(i);std::cout << "姓名: ";std::cout << people.name() << std::endl;std::cout << "年龄: " << people.age() << std::endl;std::cout << "-----------联系电话-------------" << std::endl;for (int i = 0; i < people.phones_size(); i++){contacts::PeopleInfo_Phone phone = people.phones(i);std::cout << phone.number() << std::endl;std::cout << "电话类型: " << phone.PhoneType_Name(phone.type()) << std::endl;}if (people.has_data()){std::cout << "------------地址----------------" << std::endl;google::protobuf::Any any = people.data();contacts::Address address;any.UnpackTo(&address);std::cout << "工作单位地址: " << address.util_address() << std::endl;std::cout << "家庭地址: " << address.home_address() << std::endl;std::cout << "--------------------------------" << std::endl;std::cout << "----------- 联系人--------------" << std::endl;}else{std::cout << "any is not setting!" << std::endl;}}in.close();return 0;
}
Proto3语法:
字段规则:oneof类型:
oneof类型是多选一的
//contacts.proto
例如用来定义:oneof other_contact{string qq = 5;string wechat = 6;}
//contacts。proto文件
syntax = "proto3";
package contacts;import "google/protobuf/any.proto";//地址信息
message Address{string util_address = 1;string home_address = 2;
}message PeopleInfo{string name = 1;int32 age = 2;message Phone {string number = 1;enum PhoneType{MP = 0;TEL = 1;}PhoneType type = 2;}repeated Phone phones = 3;google.protobuf.Any data = 4;//其他联系方式oneof other_contact{string qq = 5;string wechat = 6;}
}message Contacts{repeated PeopleInfo contacts = 1;
}
应用:
接口解释:
1.通过oneof设置的字段,只能填写一个,即多选一模式,以最后一次填写的字段为准
2.other_contact_case()
作用:返回内部被设置的那个字段的常量。类型为contacts::PeopleInfo::OtherContactCase
用于读取文件时判断哪个字段是有效的。
//write.ccstd::cout << "请输入其他联系方式->|1.qq---2.微信|: ";int op = 0;std::cin >> op;std::cin.ignore(256, '\n');std::string other_contact;switch (op){case 1:std::cout << "请输入qq号: ";std::getline(std::cin, other_contact);con->set_qq(other_contact);break;case 2:std::cout << "请输入微信号: ";std::getline(std::cin, other_contact);con->set_wechat(other_contact);break;default:std::cout<<"未知错误!"<<std::endl;break;}
//Read.cc// 查看其他联系方式contacts::PeopleInfo::OtherContactCase other_con = people.other_contact_case();switch (other_con){case contacts::PeopleInfo::OtherContactCase::kQq:std::cout << "QQ: " << people.qq() << std::endl;break;case contacts::PeopleInfo::OtherContactCase::kWechat:std::cout << "微信: " << people.wechat() << std::endl;break;default:std::cout << "未知错误!" << std::endl;break;}
Proto3语法:
字段规则:map类型:
1.map字段不能被repeated修饰//.proto文件
syntax = "proto3";
package contacts;import "google/protobuf/any.proto";//地址信息
message Address{string util_address = 1;string home_address = 2;
}message PeopleInfo{string name = 1;int32 age = 2;message Phone {string number = 1;enum PhoneType{MP = 0;TEL = 1;}PhoneType type = 2;}repeated Phone phones = 3;google.protobuf.Any data = 4;//其他联系方式oneof other_contact{string qq = 5;string wechat = 6;}//备注信息map<string,string> remarks = 7;
}message Contacts{repeated PeopleInfo contacts = 1;
}
应用:
接口介绍:
1.mutable_remarks()
作用:返回一块开辟好的map空间地址
2.insert()
作用:新增map类型的数据--即key-val键值对
注意:不能使用make_pair,可以使用{key,val}插入
3.remarks()
作用:获取map对象
4.begin()和cbegin()
作用:迭代器,用法和c++的map一致
//write.ccstd::cout<<"请输入该联系人的备注信息: ";google::protobuf::Map<std::string, std::string>* remark = con->mutable_remarks();for(int i = 1;;i++){std::cout<<"请输入备注标题"<<i<<"(按下空格结束): ";std::string key;std::getline(std::cin,key);if(key.size() == 0) break;std::cout<<"请输入备注内容"<<"(按下空格结束): ";std::string val;std::getline(std::cin,val);if(val.size() == 0) break;remark->insert({key,val});}
//Read.cc
std::cout<<"---------查看备注信息-----------"<<std::endl;
auto remark = people.remarks();
for(int i = 0;i<people.remarks_size();i++)
{std::cout<<"备注标题"<<i+1<<": "<< ((remark.begin())->first)<<std::endl;std::cout<<"备注内容: "<<((remark.begin())->second)<<std::endl;
}
std::cout<<"----------------------------------"<<std::endl;
Proto3语法:
字段规则:默认值:
//默认值
1.反序列化消息时,如果被反序列化的⼆进制序列中不包含某个字段,反序列化对象中相应字段时,就
会设置为该字段的默认值。不同的类型对应的默认值不同:
2.对于字符串,默认值为空字符串。
3.对于字节,默认值为空字节。
4.对于布尔值,默认值为 false。
5.对于数值类型,默认值为 0。
6.对于枚举,默认值是第⼀个定义的枚举值, 必须为 0。
7.对于消息字段,未设置该字段。它的取值是依赖于语⾔。
8.对于设置了 repeated 的字段的默认值是空的( 通常是相应语⾔的⼀个空列表 )。
9.对于 消息字段 、 oneof字段 和 any字段 ,C++ 和 Java 语⾔中都有 has_ ⽅法来检测当前字段
是否被设置。
注意:proto3语法下,标量数据类型没有生成has_方法,保证对于标量字段用户一定要手动初始化!
Proto3语法:
字段规则:更新消息:
1.如果现有的消息类型已经不再满⾜我们的需求,例如需要扩展⼀个字段,在不破坏任何现有代码的情
况下更新消息类型⾮常简单。遵循如下规则即可:
2.禁⽌修改任何已有字段的字段编号。
3.若是移除⽼字段,要保证不再使⽤移除字段的字段编号。正确的做法是保留字段编号
(reserved),以确保该编号将不能被重复使⽤。不建议直接删除或注释掉字段。
4.int32, uint32, int64, uint64 和 bool 是完全兼容的。可以从这些类型中的⼀个改为另⼀个,
⽽不破坏前后兼容性。若解析出来的数值与相应的类型不匹配,会采⽤与 C++ ⼀致的处理⽅案
(例如,若将 64 位整数当做 32 位进⾏读取,它将被截断为 32 位)。
5.sint32 和 sint64 相互兼容但不与其他的整型兼容。
6.string 和 bytes 在合法 UTF-8 字节前提下也是兼容的。
7.bytes 包含消息编码版本的情况下,嵌套消息与 bytes 也是兼容的。
8.fixed32 与 sfixed32 兼容, fixed64 与 sfixed64兼容。
9.enum 与 int32,uint32, int64 和 uint64 兼容(注意若值不匹配会被截断)。但要注意当反序
列化消息时会根据语⾔采⽤不同的处理⽅案:例如,未识别的 proto3 枚举类型会被保存在消息
中,但是当消息反序列化时如何表⽰是依赖于编程语⾔的。整型字段总是会保持其的值。
10.oneof:将⼀个单独的值更改为 新 oneof 类型成员之⼀是安全和⼆进制兼容的。
11.若确定没有代码⼀次性设置多个值那么将多个字段移⼊⼀个新 oneof 类型也是可⾏的。
12.将任何字段移⼊已存在的 oneof 类型是不安全的。
字段规则:reserved:
1.reserved修饰的编号不能被使用!
如果要删除一个已有的消息字段,不要使用其对应的字段编号,用reserved修饰即可
//例子:reserved 1;int age = 1;
此时编译不被允许!
2.reserved可以用来修饰字段名
//例子:reserved "age";int32 age = 2;//要删除的字段此时编译不被允许!
3.reserved可以用来修饰大量数字
写法1:reserved 1,2,3,...;
写法2:reserved 100 to 200;
连续修饰字段名可以用逗号分隔开
Proto3语法:
字段规则:option:
本质是影响protobuf的编译功能1.选项1:SPEEDZ:速度最快,但是占用空间更大
2.选项2:CODE_SIZE:速度最慢,空间更小,适用于包含大量的.proto文件中使用
3.选项3:LITE_RUNTIME:阉割版protobuf,仅仅提供序列化和反序列化功能+encode
4.选项4:allow_alias:允许将相同的常量值赋值给相同的枚举常量值
option allow_alias = true;
done