Proto3语法详解01

1.字段规则

消息的字段可以用下面几种规则来修饰:
●singular: 消息中可以包含该字段零次或一次(不超过一次)。proto3语法中,字段默认使用该
规则。
●repeated :消息中可以包含该字段任意多次(包括零次),其中重复值的顺序会被保留。可以理
解为定义了一个数组。
更新contacts.proto,PeopleInfo 消息中新增phone_ numbers 字段,表示一个联系人有多个
号码,可将其设置为repeated,写法如下:

syntax = "proto3"
package contacts;message PeopleInfo {string name = 1;int32 age=2;repeated string phone_number = 3;
}

2.消息类型的定义与使用

2.1定义

在单个.proto文件中可以定义多个消息体,且支持定义嵌套类型的消息(任意多层) 。每个消息体中
的字段编号可以重复。
更新contacts.proto,我们可以将phone_ number 提取出来,单独成为一一个消息:

// -------------------------- 嵌套写法 -------------------------
message PeopleInfo {string name = 1;int32 age = 2;message Phone {string number = 1;}
}
// -------------------------- 非嵌套写法 -------------------------
message Phone {string number = 1;
}
message PeopleInfo {string name = 1;int32 age = 2;
}

2.2使用

●消息类型可作为字段类型使用
contacts.proto

message PeopleInfo {string name = 1;int32 age = 2;message Phone {string number = 1;}repeated Phone phone = 3;
}

可导入其他.proto文件的消息并使用
例如Phone消息定义在phone.proto文件中:

syntax = "proto3";
package phone;message Phone {string number = 1;
}

contacts.proto中的PeopleInfo使用Phone 消息:

import "phone.proto"; // 使⽤ import 将 phone.proto ⽂件导⼊进来 !!!message PeopleInfo {string name = 1;int32 age = 2;// 引⼊的⽂件声明了package,使⽤消息时,需要⽤ ‘命名空间.消息类型’ 格式repeated phone.Phone phone = 3;
}

注:在proto3文件中可以导入proto2消息类型并使用它们,反之亦然。

2.3创建通讯录2.0版本

通讯录2.x的需求是向文件中写入通讯录列表,以上我们只是定义了一个联系人的消息,并不能存放通讯录列表,所以还需要在完善一下contacts.proto (终版通讯录2.0):

//联系人
message PeopleInfo {string name = 1;int32 age = 2;message Phone {string number = 1;}repeated Phone phone = 3;
}//通讯录:
message Contacts {repeated PeopleInfo contacts = 1;
}

接着进行一次编译:

protoc --cpp_out=. contacts.proto 

编译后生成的contacts. pb.h contacts.pb.cc 会将前面的生成文件覆盖掉。
contacts.pb.h更新的部分代码展示

// 新增了 PeopleInfo_Phone 类
class PeopleInfo_Phone final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;void CopyFrom(const PeopleInfo_Phone &from);using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;void MergeFrom(const PeopleInfo_Phone &from){PeopleInfo_Phone::MergeImpl(*this, from);}static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName(){return "PeopleInfo.Phone";}// string number = 1;void clear_number();const std::string &number() const;template <typename ArgT0 = const std::string &, typename... ArgT>void set_number(ArgT0 &&arg0, ArgT... args);std::string *mutable_number();PROTOBUF_NODISCARD std::string *release_number();void set_allocated_number(std::string *number);
};
// 更新了 PeopleInfo 类
class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;void CopyFrom(const PeopleInfo &from);using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;void MergeFrom(const PeopleInfo &from){PeopleInfo::MergeImpl(*this, from);}static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName(){return "PeopleInfo";}typedef PeopleInfo_Phone Phone;// repeated .PeopleInfo.Phone phone = 3;int phone_size() const;void clear_phone();::PeopleInfo_Phone *mutable_phone(int index);::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<::PeopleInfo_Phone> *mutable_phone();const ::PeopleInfo_Phone &phone(int index) const;::PeopleInfo_Phone *add_phone();const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<::PeopleInfo_Phone> &phone() const;
};
// 新增了 Contacts 类
class Contacts final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;void CopyFrom(const Contacts &from);using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;void MergeFrom(const Contacts &from){Contacts::MergeImpl(*this, from);}static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName(){return "Contacts";}// repeated .PeopleInfo contacts = 1;int contacts_size() const;void clear_contacts();::PeopleInfo *mutable_contacts(int index);::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<::PeopleInfo> *mutable_contacts();const ::PeopleInfo &contacts(int index) const;::PeopleInfo *add_contacts();const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<::PeopleInfo> &contacts() const;
};

上述的例子中: .
●每个字段都有一个clear_方法,可以将字段重新设置回empty状态。
●每个字段都有设置和获取的方法,获取方法的方法名称与小写字段名称完全相同。但如果是消息类型的字段,其设置方法为mutable_方法,返回值为消息类型的指针,这类方法会为我们开辟好空间,可以直接对这块空间的内容进行修改。
对于使用repeated修饰的字段,也就是数组类型,pb 为我们提供了add_方法来新增一个值,
并且提供了_ size 方法来判断数组存放元素的个数。

2.3.1通讯录2.0的写入实现

write.cc (通讯录2.0)

#include <iostream>
#include <fstream>
#include "contacts.pb.h"using namespace std;
using namespace contacts;
void AddPeopleInfo(PeopleInfo* people) {cout << "-------------新增联系⼈-------------" << endl;cout << "请输入联系人姓名:";string name;getline(cin,name);people->set_name(name);cout << "请输入联系人年龄:";int age;cin >> age;people->set_age(age);cin.ignore(256,'\n');for(int i = 1; ; ++i) {cout << "请输入联系人电话" << i << "(输入回车完成电话新增):" ;string number;getline(cin,number);if(number.empty()) break;PeopleInfo_Phone* phone = people->add_phone();phone->set_number(number);}cout << "-------------添加联系人成功-------------" << endl;}
int main()
{Contacts contacts;//先读取已经存在的contacts:fstream input("contacts.bin",ios::in | ios ::binary);if(!input) {cout << "file not exist,create new file"<< endl;} else if(!contacts.ParseFromIstream(&input)) {cerr << "Parse failed!" << endl;input.close();return -1;}//向通讯录添加一个联系人:AddPeopleInfo(contacts.add_contacts());//将通讯录写入到本地文件中:fstream output("contacts.bin",ios::out | ios :: binary | ios :: trunc);if(!contacts.SerializePartialToOstream(&output)) {cerr << "write failed !" << endl;input.close();output.close();return -1;}cout << "write sucess !" << endl;input.close();output.close();return 0;
}

makefile:

write:write.cc contacts.pb.ccg++ write.cc contacts.pb.cc -o write -std=c++11 -lprotobuf
.PHONY:clean
clean:rm -rf write

运行write:

2.3.2通讯录2.0的读取实现

read.cc (通讯录2.0)

#include <iostream>
#include <fstream>
#include "contacts.pb.h"using namespace std;
using namespace contacts;void PrintContacts(const Contacts& contacts) {for(int i = 0; i < contacts.contacts_size(); ++i) {const PeopleInfo people = contacts.contacts(i);cout << "------------联系⼈" << i+1 << "------------" << endl;cout << "联系人姓名:" << people.name() << endl;cout << "联系人年龄:" << people.age() << endl;int j = 1;for(auto& phone : people.phone()) {cout << "电话" << j++ << ": " << phone.number() << endl;}}
}
int main()
{Contacts contacts;fstream input("contacts.bin",ios::in | ios :: binary);if(!contacts.ParseFromIstream(&input)){cerr << "Parse failed!" << endl;input.close();return -1; } //打印contactsPrintContacts(contacts);input.close();return 0;
}

makefile:

all:write read
write:write.cc contacts.pb.ccg++ write.cc contacts.pb.cc -o write -std=c++11 -lprotobuf
read:read.cc contacts.pb.ccg++ read.cc contacts.pb.cc -o read -std=c++11 -lprotobuf
.PHONY:clean
clean:rm -rf write read

make后运行read

3.enum类型

3.1定义规则

语法支持我们定义枚举类型并使用。在.proto文件中枚举类型的书写规范为:
枚举类型名称:
使用驼峰命名法,首字母大写。例如: MyEnum 
常量值名称:
全大写字母,多个字母之间用_连接。 例如: ENUM_ CONST = 0;
我们可以定义一个名为PhoneType的枚举类型,定义如下:

enum PhoneType {MB = 0;TEL = 1;
}

要注意枚举类型的定义有以下几种规则:
1. 0值常量必须存在,且要作为第一-个元素。这是为了与proto2的语义兼容:第一个元素作为默认
值,且值为0。
2.枚举类型可以在消息外定义,也可以在消息体内定义(嵌套)。
3.枚举的常量值在32位整数的范围内。但因负值无效因而不建议使用(与编码规则有关)。

3.2定义时注意

将两个‘具有相同枚举值 名称’的枚举类型放在 单个.proto文件下测试时,编译后会报错:某某某常
量已经被定义!所以这里要注意:
●同级(同层)的枚举类型,各个枚举类型中的常量不能重名。
●单个.proto 文件下,最外层枚举类型和嵌套枚举类型,不算同级。
●多个.proto文件下,若一个文件引入了其他文件,且每个文件都未声明package,每个proto文
件中的枚举类型都在最外层,算同级。
●多个.proto文件下,若一个文件引入了其他文件,且每个文件都声明了package,不算同级。

// ---------------------- 情况1:同级枚举类型包含相同枚举值名称--------------------
enum PhoneType
{MP = 0;  // 移动电话TEL = 1; // 固定电话
}
enum PhoneTypeCopy
{MP = 0; // 移动电话 // 编译后报错:MP 已经定义
}
// ---------------------- 情况2:不同级枚举类型包含相同枚举值名称-------------------
enum PhoneTypeCopy
{MP = 0; // 移动电话 // ⽤法正确
}
message Phone
{string number = 1; // 电话号码enum PhoneType{MP = 0;  // 移动电话TEL = 1; // 固定电话}
}
// ---------------------- 情况3:多⽂件下都未声明package--------------------
// phone1.proto
import "phone1.proto" 
enum PhoneType
{MP = 0; // 移动电话 // 编译后报错:MP 已经定义TEL = 1;    // 固定电话
}
// phone2.proto
enum PhoneTypeCopy
{MP = 0; // 移动电话
}
// ---------------------- 情况4:多⽂件下都声明了package--------------------
// phone1.proto
import "phone1.proto" package phone1;
enum PhoneType
{MP = 0;  // 移动电话 // ⽤法正确TEL = 1; // 固定电话
}
// phone2.proto
package phone2;
enum PhoneTypeCopy
{MP = 0; // 移动电话
}

3.3升级通讯录至2.1版本

更新contacts.proto (通讯录2.1),新增枚举字段并使用,更新内容如下:

//联系人
message PeopleInfo {string name = 1;int32 age = 2;message Phone {string number = 1;enum PhoneType {MB = 0;TEL = 1;}PhoneType type = 2;        }repeated Phone phone = 3;
}//通讯录:
message Contacts {repeated PeopleInfo contacts = 1;
}

编译:

protoc --cpp_out=. contacts.proto

// 新⽣成的 PeopleInfo_Phone_PhoneType 枚举类
enum PeopleInfo_Phone_PhoneType : int
{PeopleInfo_Phone_PhoneType_MP = 0,PeopleInfo_Phone_PhoneType_TEL = 1,PeopleInfo_Phone_PhoneType_PeopleInfo_Phone_PhoneType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),PeopleInfo_Phone_PhoneType_PeopleInfo_Phone_PhoneType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
};
// 更新的 PeopleInfo_Phone 类
class PeopleInfo_Phone final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:typedef PeopleInfo_Phone_PhoneType PhoneType;static inline bool PhoneType_IsValid(int value){return PeopleInfo_Phone_PhoneType_IsValid(value);}template <typename T>static inline const std::string &PhoneType_Name(T enum_t_value) { ... }static inline bool PhoneType_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name, PhoneType *value) { ... }// .contacts.PeopleInfo.Phone.PhoneType type = 2;void clear_type();::contacts::PeopleInfo_Phone_PhoneType type() const;void set_type(::contacts::PeopleInfo_Phone_PhoneType value);
};

上述的代码中:
对于在.proto文件中定义的枚举类型,编译生成的代码中会含有与之对应的枚举类型、校验枚举
值是否有效的方法__IsValid、 以及获取枚举值名称的方法_ Name。
●对于使用了枚举类型的字段,包含设置和获取字段的方法,已经清空字段的方法clear__。

更新write.cc(通讯录2.1)

#include <iostream>
#include <fstream>
#include "contacts.pb.h"using namespace std;
using namespace contacts;
void AddPeopleInfo(PeopleInfo *people)
{cout << "-------------新增联系⼈-------------" << endl;cout << "请输入联系人姓名:";string name;getline(cin, name);people->set_name(name);cout << "请输入联系人年龄:";int age;cin >> age;people->set_age(age);cin.ignore(256, '\n');for (int i = 1;; ++i){cout << "请输入联系人电话" << i << "(输入回车完成电话新增):";string number;getline(cin, number);if (number.empty())break;PeopleInfo_Phone *phone = people->add_phone();phone->set_number(number);//更新代码:cout << "选择此电话类型 (1、移动电话 2、固定电话) : " ;int type;cin >> type;cin.ignore(256,'\n');switch (type) {case 1:phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MB);break;case 2:phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL);break;default:cout << "输入有误,请重新输入" << endl;}}cout << "-------------添加联系人成功-------------" << endl;
}
int main()
{Contacts contacts;// 先读取已经存在的contacts:fstream input("contacts.bin", ios::in | ios ::binary);if (!input){cout << "file not exist,create new file" << endl;}else if (!contacts.ParseFromIstream(&input)){cerr << "Parse failed!" << endl;input.close();return -1;}// 向通讯录添加一个联系人:AddPeopleInfo(contacts.add_contacts());// 将通讯录写入到本地文件中:fstream output("contacts.bin", ios::out | ios ::binary | ios ::trunc);if (!contacts.SerializePartialToOstream(&output)){cerr << "write failed !" << endl;input.close();output.close();return -1;}cout << "write sucess !" << endl;input.close();output.close();return 0;
}

更新read.cc(通讯录2.1)

#include <iostream>
#include <fstream>
#include "contacts.pb.h"using namespace std;
using namespace contacts;void PrintContacts(const Contacts& contacts) {for(int i = 0; i < contacts.contacts_size(); ++i) {const PeopleInfo people = contacts.contacts(i);cout << "------------联系⼈" << i+1 << "------------" << endl;cout << "联系人姓名:" << people.name() << endl;cout << "联系人年龄:" << people.age() << endl;int j = 1;for(auto& phone : people.phone()) {cout << "电话" << j++ << ": " << phone.number();//更新代码:cout << " (" << phone.PhoneType_Name(phone.type()) << ")" << endl;}}
}
int main()
{Contacts contacts;fstream input("contacts.bin",ios::in | ios :: binary);if(!contacts.ParseFromIstream(&input)){cerr << "Parse failed!" << endl;input.close();return -1; } //打印contactsPrintContacts(contacts);input.close();return 0;
}

运行截图: 

4. Any类型

字段还可以声明为Any类型,可以理解为泛型类型。使用时可以在Any中存储任意消息类型。Any类型的字段也用repeated来修饰。
Any类型是google已经帮我们定义好的类型,在安装ProtoBuf时,其中的include目录下查找所有
google已经定义好的.proto文件。

4.1升级通讯录至2.2版本

通讯录2.2版本会新增联系人的地址信息,我们可以使用any类型的字段来存储地址信息。
更新contacts.proto (通讯录2.2),更新内容如下:

import "google/protobuf/any.proto"; //引入any.proto文件
//地址:
message Address {string home_address = 1; //家庭住址string unit_address = 2; //单位住址
}
//联系人
message PeopleInfo {string name = 1;int32 age = 2;message Phone {string number = 1;enum PhoneType {MB = 0;TEL = 1;}PhoneType type = 2;        }repeated Phone phone = 3;google.protobuf.Any data = 4;
}//通讯录:
message Contacts {repeated PeopleInfo contacts = 1;
}

编译

protoc --cpp_ out=. contacts.proto

contacts.pb.h更新的部分代码展示:

// 新⽣成的 Address 类
class Address final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;void CopyFrom(const Address &from);using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;void MergeFrom(const Address &from){Address::MergeImpl(*this, from);}// string home_address = 1;void clear_home_address();const std::string &home_address() const;template <typename ArgT0 = const std::string &, typename... ArgT>void set_home_address(ArgT0 &&arg0, ArgT... args);std::string *mutable_home_address();PROTOBUF_NODISCARD std::string *release_home_address();void set_allocated_home_address(std::string *home_address);// string unit_address = 2;void clear_unit_address();const std::string &unit_address() const;template <typename ArgT0 = const std::string &, typename... ArgT>void set_unit_address(ArgT0 &&arg0, ArgT... args);std::string *mutable_unit_address();PROTOBUF_NODISCARD std::string *release_unit_address();void set_allocated_unit_address(std::string *unit_address);
};
// 更新的 PeopleInfo 类
class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:// .google.protobuf.Any data = 4;bool has_data() const;void clear_data();const ::PROTOBUF_NAMESPACE_ID::Any &data() const;PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::Any *release_data();::PROTOBUF_NAMESPACE_ID::Any *mutable_data();void set_allocated_data(::PROTOBUF_NAMESPACE_ID::Any *data);
};

上述的代码中,对于Any类型字段:
●设置和获取:获取方法的方法名称与小写字段名称完全相同。设置方法可以使用mutable_ 方
法,返回值为Any类型的指针,这类方法会为我们开辟好空间,可以直接对这块空间的内容进行
修改。
之前讲过,我们可以在Any字段中存储任意消息类型,这就要涉及到任意消息类型和Any类型的互
转。这部分代码就在Google为我们写好的头文件any.pb.h中。对any.pb. h部分代码展示:

class PROTOBUF_EXPORT Any final : public ::PROTOBUF_NAMESPACE_ID::Message
{bool PackFrom(const ::PROTOBUF_NAMESPACE_ID::Message &message){...}bool UnpackTo(::PROTOBUF_NAMESPACE_ID::Message *message) const{...}template <typename T>bool Is() const{return _impl_._any_metadata_.Is<T>();}
};

解释:
使用 PackFrom() ⽅法可以将任意消息类型转为 Any 类型。
    使用 UnpackTo() ⽅法可以将 Any 类型转回之前设置的任意消息类型。
    使用 Is() 方法可以用来判断存放的消息类型是否为 typename T。

更新write.cc(通讯录2.2)

        Address address;cout << "请输入联系人家庭地址:";string home_address;getline(cin,home_address);cin.ignore(256,'\n');address.set_home_address(home_address);cout << "请输入联系人单位地址:";string unit_address;getline(cin,unit_address);cin.ignore(256,'\n');address.set_unit_address(unit_address);google::protobuf::Any* data = people->mutable_data();data->PackFrom(address);

更新read.cc(通讯录2.2):

        if(people.has_data() && people.data().Is<Address>()) {Address address;people.data().UnpackTo(&address);if(!address.home_address().empty()) {cout << "家庭住址:" << address.home_address() << endl;}if(!address.unit_address().empty()) {cout << "家庭住址:" << address.unit_address() << endl;}}

运行截图:

5. oneof类型

如果消息中有很多可选字段,并且将来同时只有一个字段会被设置,那么就可以使用oneof 加强这
个行为,也能有节约内存的效果。

5.1升级通讯录至2.3版本

通讯录2.3版本想新增联系人的其他联系方式,比如qq或者微信号二选一, 我们就可以使用oneof字段来加强多选一这个行为。oneof 字段定义的格式为: oneof 字段名{字段1;字段2; ... }
更新contacts.proto (通讯录2.3),更新内容如下:

mport "google/protobuf/any.proto"; //引入any.proto文件
//地址:
message Address {string home_address = 1; //家庭住址string unit_address = 2; //单位住址
}
//联系人
message PeopleInfo {string name = 1;int32 age = 2;message Phone {string number = 1;enum PhoneType {MB = 0;TEL = 1;}PhoneType type = 2;        }repeated Phone phone = 3;google.protobuf.Any data = 4;oneof other_Ways {string qq = 5;string weixin = 6;}
}//通讯录:
message Contacts {repeated PeopleInfo contacts = 1;
}

注意:
●可选字段中的字段编号,不能与非可选字段的编号冲突。
不能在oneof中使用repeated字段。
●将来在设置oneof字段中值时,如果将oneof中的字段设置多个,那么只会保留最后一次设置的成
员,之前设置的oneof成员会自动清除。

编译

protoc --cpp_ out=. contacts.proto

contacts.pb.h更新的部分代码展示:

// 更新的 PeopleInfo 类
class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message {enum OtherContactCase {kQq = 5,kWeixin = 6,OTHER_CONTACT_NOT_SET = 0,};// string qq = 5;bool has_qq() const;void clear_qq();const std::string& qq() const;template <typename ArgT0 = const std::string&, typename... ArgT>void set_qq(ArgT0&& arg0, ArgT... args);std::string* mutable_qq();PROTOBUF_NODISCARD std::string* release_qq();void set_allocated_qq(std::string* qq);// string weixin = 6;bool has_weixin() const;void clear_weixin();const std::string& weixin() const;template <typename ArgT0 = const std::string&, typename... ArgT>void set_weixin(ArgT0&& arg0, ArgT... args);std::string* mutable_weixin();PROTOBUF_NODISCARD std::string* release_weixin();void set_allocated_weixin(std::string* weixin);void clear_other_contact();OtherContactCase other_contact_case() const;
};

上述的代码中,对于oneof字段:
●会将 oneof中的多个字段定义为一个枚举类型。
●设置和获取:对oneof内的字段进行常规的设置和获取即可,但要注意只能设置- -个。 如果设置
多个,那么只会保留最后一次设置的成员。
●清空oneof字段: clear_ 方法
●获取当前设置了哪个字段:_ case方法

 更新write.cc (通讯录2.3),更新内容如下:

        cout << "选择添加⼀个其他联系⽅式 (1、qq号 2、微信号) : " ;int other_contacts;cin >> other_contacts;cin.ignore(256,'\n');if(1 == other_contacts) {cout << "请输入qq号: " ;string qq;getline(cin,qq);cin.ignore(256,'\n');people->set_qq(qq);} else if(2 == other_contacts) {cout << "请输入微信号:";string weixin;getline(cin,weixin);cin.ignore(256,'\n');people->set_weixin(weixin);} else {cout << "非法选择,设置失败" << endl;}

更新read.cc (通讯录2.3),更新内容如下:

       switch (people.other_Ways_case()) {case PeopleInfo::OtherWaysCase::kQq: cout << "qq号: " << people.qq() << endl;break;case PeopleInfo::OtherWaysCase::kWeixin:cout << "微信号:" << people.weixin() << endl;break;case PeopleInfo::OtherWaysCase::OTHER_WAYS_NOT_SET:break;}

运行截图:

6. map类型

语法支持创建一个关联映射字段,也就是可以使用map类型去声明字段类型,格式为:
map<key_ type, value_type> map _field = N;
要注意的是:
●key_ type是除了float和bytes类型以外的任意标量类型。 value_type可以是任意类型。
map字段不可以用repeated修饰
●map中存入的元素是无序的

6.1升级通讯录至2.4版本

最后,通讯录2.4版本想新增联系人的备注信息,我们可以使用map类型的字段来存储备注信息。
更新contacts.proto (通讯录2.4),更新内容如下:

import "google/protobuf/any.proto"; //引入any.proto文件
//地址:
message Address {string home_address = 1; //家庭住址string unit_address = 2; //单位住址
}
//联系人
message PeopleInfo {string name = 1;int32 age = 2;message Phone {string number = 1;enum PhoneType {MB = 0;TEL = 1;}PhoneType type = 2;        }repeated Phone phone = 3;google.protobuf.Any data = 4;oneof other_Ways {string qq = 5;string weixin = 6;}map<string,string> remark = 7; //备注
}//通讯录:
message Contacts {repeated PeopleInfo contacts = 1;
}

编译

protoc --cpp_ out=. contacts.proto

contacts.pb.h更新的部分代码展示:

// 更新的 PeopleInfo 类
class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message {// map<string, string> remark = 7;int remark_size() const;void clear_remark();const ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >&remark() const;::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >*mutable_remark();
};

上述的代码中,对于Map类型的字段: .
●清空map: clear_ 方法
●设置和获取:获取方法的方法名称与小写字段名称完全相同。设置方法为mutable_ 方法,返回
值为Map类型的指针,这类方法会为我们开辟好空间,可以直接对这块空间的内容进行修改。
更新write.cc (通讯录2.4),更新内容如下:

            for(int i = 1;; ++i) {cout << "请输⼊备注" << i << "标题 (只输入回车完成备注): ";string remark_key;getline(cin,remark_key);if(remark_key.empty()) {break;}cout << "请输入备注:" << i << "内容:";string remark_value;getline(cin,remark_value);people->mutable_remark()->insert({remark_key,remark_value});}

更新read.cc (通讯录2.4),更新内容如下:

        if(people.remark_size()) {cout << "备注信息:" << endl;}for(auto it = people.remark().cbegin(); it != people.remark().cend(); ++it) {cout << "   " << it->first << "   " << it->second << endl;}

运行截图:

到此,我们对通讯录2.x要求的任务全部完成。在这个过程中我们将通讯录升级到了2.4 版本,同时对ProtoBuf的使用也进一步熟练了 ,并且也掌握了ProtoBuf的proto3语法支持的大部分类型及其使
用,但只是正常使用还是完全不够的。通过接下来的学习,我们就能更进一-步了解到ProtoBuf深入的内容。

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

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

相关文章

MYSQL基础知识之【数据类型】

文章目录 前言标题一数值类型日期和时间类型字符串类型后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Mysql &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现错…

基于51单片机的FM数字收音机系统电路设计

**单片机设计介绍&#xff0c;基于51单片机的FM数字收音机系统电路设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机的FM数字收音机系统是一种用于接收和播放FM广播信号的设备&#xff0c;以下是一个基本的电路设…

LeetCode OJ循环队列(C语言)

1.题目的初步分析 我们分析上述题目的时候会发现题目非常的长&#xff0c;不好整理思路&#xff0c;我这里可以大致的将本题的几个核心点说出来&#xff1a; 1.队列的思路 循环队列说来说去不还是队列嘛&#xff0c;那么队列的基本操作增删查改、以及队列的基本结构肯定都是不能…

sql查询优化实际案例

1、第一步&#xff1a;sql优化 正对于海量数据的查询优化&#xff0c;且外键关联比较多的情况&#xff0c;通常情况是下sql层面的优化&#xff0c;有些时候是由于sql不合理的编写导致&#xff0c;如尽量少使用sql内查询等 如&#xff1a;避免使用 left join (select * form …

排序算法-----基数排序

目录 前言 基数排序 算法思想 ​编辑 算法示例 代码实现 1.队列queue.h 头文件 2.队列queue.c 源文件 3.主函数&#xff08;radix_sort实现&#xff09; 算法分析 前言 今天我想把前面未更新完的排序算法补充一下&#xff0c;也就是基数排序的一种&#xff0c;这是跟…

【数据库】数据库物理执行计划最基本操作-表扫描机制与可选路径,基于代价的评估模型以及模型参数的含义

物理执行计划基本操作符 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏…

css三角,鼠标样式,溢出文字

目录 css三角 鼠标样式 例子&#xff1a;页码模块 溢出文字表示方式 margin负值运用 css三角强化 css三角 css三角中&#xff1a;line-height&#xff1a;0和font-size&#xff1a;0是防止兼容性的问题 jd {position: relative;width: 120px;height: 249px;background-…

斐讯K2结合Padavan实现锐捷认证破解方法

前言 众所周知&#xff0c;校园网在传统模式下是不能直接插路由使用的&#xff0c;但苦于校园网只能连接一台设备的烦恼&#xff0c;不得不“另辟蹊径”来寻求新的解决路径&#xff0c;这不&#xff0c;它来了&#xff0c;它来了&#xff0c;它带着希望走来了。 本文基于斐讯…

电子学会C/C++编程等级考试2021年09月(二级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:字符统计 给定一个由a-z这26个字符组成的字符串,统计其中哪个字符出现的次数最多。输入 输入包含一行,一个字符串,长度不超过1000。输出 输出一行,包括出现次数最多的字符和该字符出现的次数,中间以一个空格分开。如果有多…

前缀和——238. 除自身以外数组的乘积

文章目录 &#x1f377;1. 题目&#x1f378;2. 算法原理&#x1f365;解法一&#xff1a;暴力求解&#x1f365;解法二&#xff1a;前缀和&#xff08;积&#xff09; &#x1f379;3. 代码实现 &#x1f377;1. 题目 题目链接&#xff1a;238. 除自身以外数组的乘积 - 力扣&a…

34970A 数据采集 / 数据记录仪开关单元

34970A 数据采集 / 数据记录仪开关单元 产品综述&#xff1a; Keysight 34970A 数据采集/数据记录仪开关单元由一个 3 插槽主机和一个内置的 6 1/2 位数字万用表组成。每个通道可以单独配置&#xff0c;以测量 11 种不同功能之一&#xff0c;这样既不会增加成本&#xff0c;也…

ubuntu挂载硬盘方法

1.关闭服务器加上新硬盘 2.启动服务器&#xff0c;以root用户登录 3.查看硬盘信息 fdisk -l4.格式化分区 找到需要分区的目录,并记录分区的uuid&#xff0c;用于后面修改/etc/fstab永久挂载配置文件 mkfs.ext4 /dev/nvme0n1 mkfs.ext4 /dev/nvme1n1 Filesystem UUID: a1c…