Google第三方库详解------ProtoBuf详解 + 样例(5万字详解!)

目录

前言:

提示:

插件

入门:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/22987.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

goredis常见基础命令

基本操作 //删除键 exists,err: rdb.Exists(ctx,"key").Result() if err!nil{panic(err) } if exists>0{err rdb.Del(ctx,"key").Err()if err!nil{panic(err)} }string类型 //设置一个键值对 //0表示没有过期时间 err:rdb.Set(ctx,"key1",…

微服务环境搭建架构介绍(附超清图解源代码)

微服务介绍 系统架构演变 随着互联网的发展&#xff0c;网站应用的规模也在不断的扩大&#xff0c;进而导致系统架构也在不断的进行变化。 从互联网早起到现在&#xff0c;系统架构大体经历了下面几个过程: 单体应用架构--->垂直应用架构--->分布 式架构--->SOA架构…

Java-01-源码篇-04集合-05-ConcurrentHashMap(1)

1.1 加载因子 加载因子&#xff08;Load Factor&#xff09;是用来决定什么时候需要扩容的一个参数。具体来说&#xff0c;加载因子 当前元素数量 / 桶的数量&#xff0c;当某个桶的元素个数超过了 桶的数量 加载因子 时&#xff0c;就会触发扩容。 我们都知道 ConcurrentHas…

一文详解U盘启动Legacy/UEFI方式以及GPT/MBR关系

对于装系统的老手而说一直想研究一下装系统的原理&#xff0c;以及面对一些问题时的解决思路&#xff0c;故对以前的方法进行原理上的解释&#xff0c;主要想理解其底层原理。 引导模式 MBR分区可以同时支持UEFI和Legacy引导&#xff0c;我们可以看一下微pe制作的启动盘&#…

【多线程-第三天-NSOperation的练习-tableView异步下载网络图片-下载操作缓存池 Objective-C语言】

一、下载操作缓存池 1.下面我们来看操作缓存池,我们先演示一下问题,看看为什么要加这么一个操作缓存池,什么是操作缓存池,不用管呢,我们先来看啊,首先有什么问题, 看这个问题之前,我这儿写一个touch,点击屏幕的时候调用, 额,不能点击屏幕啊,因为现在屏幕点不着,我…

Windows 中的启动项如何打开?管理电脑启动程序的三种方法

在日常使用电脑时&#xff0c;我们经常会发现一些应用程序在开机时自动启动&#xff0c;这不仅会拖慢系统的启动速度&#xff0c;还可能占用不必要的系统资源。幸运的是&#xff0c;通过几个简单的步骤&#xff0c;你可以轻松管理这些开机自启的应用程序。接下来&#xff0c;我…

具备智能广告拦截、个性化定制的便捷网页浏览器

软件介绍 今天要给大家介绍一款源自俄罗斯的国民级软件&#xff0c;它来自俄罗斯最大互联网公司之一的 Yandex。这家公司不仅有搜索引擎业务&#xff0c;还打造出诸多热门软件&#xff0c;其中就有我们要讲的这款网页浏览器。它由 Yandex 公司依托 Chromium 开源项目开发&…

LangChain-基础(prompts、序列化、流式输出、自定义输出)

LangChain-基础 我们现在使用的大模型训练数据都是基于历史数据训练出来的&#xff0c;它们都无法处理一些实时性的问题或者一些在训练时为训练到的一些问题&#xff0c;解决这个问题有2种解决方案 基于现有的大模型上进行微调&#xff0c;使得它能适应这些问题&#xff08;本…

119. 杨辉三角 II

给定一个非负索引 rowIndex&#xff0c;返回「杨辉三角」的第 rowIndex 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: rowIndex 3 输出: [1,3,3,1]示例 2: 输入: rowIndex 0 输出: [1]示例 3: 输入: rowIndex 1 输出: [1,1]提示…

Unity Android SDK 升级、安装 build-tools、platform-tools

Unity Android SDK 升级、安装 build-tools、platform-tools 通过 Unity Hub 安装的 Android SDK 需要下载 特定版本的 build-tools、platform-tools 如何操作&#xff1f; 以 Unity 2022.3.26f1 为例&#xff0c;打开安装目录&#xff0c;找到如下目录 2022.3.26f1\Editor\…

网络空间安全(3)web渗透测试学习框架

前言 Web渗透测试是一种安全评估方法&#xff0c;旨在通过模拟黑客攻击来检测Web应用程序中的安全漏洞。 一、学习基础 在学习Web渗透测试之前&#xff0c;需要掌握一些基础知识&#xff0c;包括计算机网络、Web开发技术&#xff08;如HTML、JavaScript、PHP等&#xff09;、数…

人工智能之自动驾驶技术体系

自动驾驶技术体系 自动驾驶技术是人工智能在交通领域的重要应用&#xff0c;旨在通过计算机视觉、传感器融合、路径规划等技术实现车辆的自主驾驶。自动驾驶不仅能够提高交通效率&#xff0c;还能减少交通事故和环境污染。本文将深入探讨自动驾驶的技术体系&#xff0c;包括感…

25会计研究生复试面试问题汇总 会计专业知识问题很全! 会计复试全流程攻略 会计考研复试真题汇总

宝子们&#xff0c;会计考研复试快到了&#xff0c;是不是有点慌&#xff1f;别怕&#xff01;今天学姐给你们支招&#xff0c;手把手教你搞定复试面试&#xff0c;直接冲上岸&#xff01;快来看看怎么准备吧&#xff0c;时间紧直接背第三部分的面试题&#xff01; 目录 一、复…

本地化部署 DeepSeek:从零到一的完整指南

本地化部署 DeepSeek&#xff1a;从零到一的完整指南 个人主页&#xff1a;顾漂亮 文章专栏&#xff1a;AI学习 目录 引言什么是 DeepSeek&#xff1f;为什么选择本地化部署&#xff1f;DeepSeek 本地化部署的前期准备 硬件需求软件需求环境配置 DeepSeek 本地化部署步骤 步骤…

【深度学习】Unet的基础介绍

U-Net是一种用于图像分割的深度学习模型&#xff0c;特别适合医学影像和其他需要分割细节的任务。如图&#xff1a; Unet论文原文 为什么叫U-Net&#xff1f; U-Net的结构像字母“U”&#xff0c;所以得名。它的结构由两个主要部分组成&#xff1a; 下采样&#xff08;编码…

【学习笔记】Cadence电子设计全流程(二)原理图库的创建与设计(8-15)

【学习笔记】Cadence电子设计全流程&#xff08;二&#xff09;原理图库的创建与设计&#xff08;下&#xff09; 2.8 Cadence 软件自带元件库2.9 原理图元器件关联PCB2.10 原理图元器件库的移植2.11 已有原理图输出元器件库2.12 原理图设计中调用元器件库2.13 原理图元器件库关…

DeepSeek从入门到精通

1_DeepSeek从入门到精通 (1).pdf官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘123云盘为您提供1_DeepSeek从入门到精通 (1).pdf最新版正式版官方版绿色版下载,1_DeepSeek从入门到精通 (1).pdf安卓版手机版apk免费下载安装到手机,支持电脑端一键快捷安装https://www.123…

Comfyui Windows Desktop桌面版便携版安装教程

前段时间Comfyui 的便携包安装写了一篇&#xff0c;最近comfyui发布了新的桌面版本0.4.5&#xff0c;我也试着安装了一下&#xff0c;感觉使用体验比便携包要舒适一点点。 下面是安装指南。 安装地址 官方给了下载包&#xff0c;分为N卡和Mac。地址&#xff1a;Notion – Th…

DeepSeek 提示词:定义、作用、分类与设计原则

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

【Linux网络编程】数据链路层和网络层的几个问题:MTU,校验和,全球网段,路由表

目录 1.MTU 2.CRC校验和 3.全球网段 4.子网掩码 5.路由 1.MTU MTU是以太网的最大传输单位&#xff0c;大小是1500字节&#xff0c;表示IP&#xff08;网络层传下来的最多只能1500字节&#xff09;。 如果超过了这个数&#xff0c;就要网络层自己做分包。数据链路层是不帮…