protobuf的简单使用(通讯录例子)
.proto文件的编写
- syntax用于指定protobuf的语法;
- package当.proto文件编译后再*.pb.h文件中会形成一个命名空间。
- message+消息名称:当.proto文件编译后再*.pb.h文件中会形成一个类。
保留字段
- 可以看到每一个属性后面会有一个数字编号,可以看成属性的唯一标识,用来在消息的二进制格式中标识属性,同一个message域里面不可以使用冲突。定义了就不能修改了,若是想删除,就要将编号添加进reserved保留字段,并且该字段不可继续使用。例如:
message PeopleInfo{
//可以单个指定保留字段,也可指定一个范围的保留字段:
// 13 to 20 包含13和20。
reserved 10, 11, 13 to 20;
// 也可指明字段名
reserved "gender";
string field1 = 10;
string field2 = 11;
string field3 = 15;
string gender = 9;
string name = 1;
uint32 age = 2;
message Phone{
string number = 1;
enum PhoneType{
MP = 0;
TEL = 1;
}
PhoneType type = 2;
}
repeated Phone phone = 3;
google.protobuf.Any data = 4;
oneof other_contact{
string qq = 5;
string wechat = 6;
}
map<string, string> remark = 7;
}
如上可以看到,定义了保留字段,编译后如下:
表示保留字段不可继续使用。
字段唯一编号
另外,字段唯一编号范围为:1 ~ 536870911(2^29-1),其中19000 ~ 19999不可用。如果使用了就会告警。
指定字段唯一编号时,对于使用频繁的字段,将其唯一编号设置为1 ~ 15, 因为1 ~ 15的字段编号只需要一个字节进行编码,16~2047以内的需要2个字节。
protobuf的类型
.proto Type字段是.proto文件中的字段类型, C++ Type是.proto文件编译后形成的*.pb.h和 *.pb.c文件中的c++对应类型。
enum类型
- enum类型可以定义在消息体内,也可以定义在消息体外。
- 枚举的常量值在32整数范围内。
- 0值常量必须存在,且要作为第一个元素。
- 同级,也就是同一个作用域中,各个枚举类型中不能定义相同枚举名称:
Any类型
any类型可以理解为泛型类型,可以在any中存储任意数据类型,any类型也可以用repeated修饰。
在安装protobuf时,include目录下包含了google定义好的.proto文件,在使用any类型时,需要引入any文件,使用import
即可。如:
import "google/protobuf/any.proto";
定义any类型时,只需使用google.protobuf.Any
当做类型名来定义即可,具体的任意类型和any类型的绑定不在.proto文件中编码。
例如:
.proto文件中希望将Address类型转换成any类型:
message Address{
string home_address = 1;
string unit_address = 2;
}
message PeopleInfo{
google.protobuf.Any data = 4;
}
上面的代码为部分截取。
在进行序列化的.cc文件中可以这样编写:
Address address;
cout << "请输入家庭地址:";
string home_address;
getline(cin, home_address);
cout << "请输入单位地址:";
string unit_address;
getline(cin, unit_address);
address.set_home_address(home_address);
address.set_unit_address(unit_address);
// 使用mutable_方法会开辟空间,并且返回一个any指针
// people_info_ptr是PeopleInfo的指针.
google::protobuf::Any *data = people_info_ptr->mutable_data();
PackFrom可以将任意类型转换为any类型。
data->PackFrom(address);
- 使⽤ PackFrom() ⽅法可以将任意消息类型转为 Any 类型。
- 使⽤ UnpackTo() ⽅法可以将 Any 类型转回之前设置的任意消息类型
- 使⽤ Is() ⽅法可以⽤来判断存放的消息类型是否为 typename T。
oneof类型
顾名思义,就是在众多类型中选择一个类型进行使用。
例如:
oneof other_contact{
string qq = 5;
string wechat = 6;
}
- oneof中类型的唯一编号,不能和other_contact所在的作用域的编号起冲突,要有唯一性。
- oneof中不能使用repeated。
- 在设置oneof中的值的时候,如果多次进行设置值,那么只会保留最后一次设置的值。
- 获取当前设置了哪一个字段:使用_case方法。
由于多次进行设置值,那么只会保留最后一次设置的值。这一特性,那么我们在设置oneof值的时候:
cout << "选择一个其他联系方式(1,QQ号 2, 微信号): ";
int other_contact;
cin >> other_contact;
cin.ignore(256, '\n');
if(1 == other_contact)
{
cout << "请输入QQ号:";
string qq;
getline(cin, qq);
people_info_ptr->set_qq(qq);
}
else if(2 == other_contact)
{
cout << "请输入微信号:" ;
string wechat;
getline(cin, wechat);
people_info_ptr->set_wechat(wechat);
}
else{
cout << "非法选择,该选项设置失败。" << endl;
}
当我们在读取的时候:
switch(people.other_contact_case())
{
case PeopleInfo::OtherContactCase::kQq:
cout << "qq号:" << people.qq() <<endl;
break;
case PeopleInfo::OtherContactCase::kWechat:
cout << "微信号:" << people.wechat() << endl;
break;
}
map类型
map<key_type, value_type> map_field = N;
跟c++的map差不多,用于key_type和value_type的关系映射。
- key_type是除了float和bytes类型以外的任意标量类型。value_type可以是任意类型。
- map不可以用repeated修饰。
- map存入的元素是无序的。
//write:
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_info_ptr->mutable_remark()->insert({remark_key, remark_value});
}
// read:
if(people.remark_size())
{
cout << "备注信息:" << endl;
}
for(auto it = people.remark().cbegin(); it != people.remark().cend(); ++it)
{
cout << " " << it->first << ": " << it->second << endl;
}
完整通讯录代码
.proto文件
syntax = "proto3";
package contacts;
import "google/protobuf/any.proto";
message Address{
string home_address = 1;
string unit_address = 2;
}
message PeopleInfo{
string name = 1;
uint32 age = 2;
message Phone{
string number = 1;
enum PhoneType{
MP = 0;
TEL = 1;
}
PhoneType type = 2;
}
repeated Phone phone = 3;
google.protobuf.Any data = 4;
oneof other_contact{
string qq = 5;
string wechat = 6;
}
map<string, string> remark = 7;
}
message Contacts{
repeated PeopleInfo contacts = 1;
}
write文件
#include <iostream>
#include <fstream>
#include "contacts.pb.h"
using namespace std;
using namespace contacts;
void AddPeopleInfo(PeopleInfo *people_info_ptr)
{
cout << "---------------新增联系人-------------------" << endl;
cout << "请输入联系人姓名:";
string name;
getline(cin, name);
people_info_ptr->set_name(name);
cout << "请输入联系人年龄:";
int age;
cin >> age;
people_info_ptr->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_info_ptr->add_phone();
phone->set_number(number);
cout << "选择此电话类型(1, 移动电话 2, 固定电话): ";
int type;
cin >> type;
cin.ignore(256, '\n');
switch(type)
{
case 1:
phone->set_type(PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP);
break;
case 2:
phone->set_type(PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL);
break;
default:
cout << "非法选择,使用默认值" << endl;
break;
}
}
Address address;
cout << "请输入联系人家庭地址:";
string home_address;
getline(cin, home_address);
address.set_home_address(home_address);
cout << "请输入联系人单位地址:";
string unit_address;
getline(cin, unit_address);
address.set_unit_address(unit_address);
google::protobuf::Any *data = people_info_ptr->mutable_data();
data->PackFrom(address);
cout << "选择一个其他联系方式(1,QQ号 2, 微信号): ";
int other_contact;
cin >> other_contact;
cin.ignore(256, '\n');
if(1 == other_contact)
{
cout << "请输入QQ号:";
string qq;
getline(cin, qq);
people_info_ptr->set_qq(qq);
}
else if(2 == other_contact)
{
cout << "请输入微信号:" ;
string wechat;
getline(cin, wechat);
people_info_ptr->set_wechat(wechat);
}
else{
cout << "非法选择,该选项设置失败。" << endl;
}
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_info_ptr->mutable_remark()->insert({remark_key, remark_value});
}
cout << "---------------添加联系人成功----------------" << endl;
}
int main()
{
Contacts contacts;
fstream input("./contacts.bin", ios::in | ios::binary);
if(!input)
{
cerr << "contacts.bin file no found" << endl;
return -1;
}
else if(!contacts.ParseFromIstream(&input))
{
cerr << "Failed to parse contacts" << endl;
return -1;
}
AddPeopleInfo(contacts.add_contacts());
fstream output("./contacts.bin", ios::out | ios::trunc | ios::binary);
if(!contacts.SerializeToOstream(&output))
{
cerr << "Failed to write contacts." << endl;
input.close();
output.close();
return -1;
}
input.close();
output.close();
return 0;
}
read文件
#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 = 0;
for(const PeopleInfo_Phone &phone : people.phone())
{
cout << "电话:" << j++ << phone.number() ;
cout << " (" << phone.PhoneType_Name(phone.type()) << ")" << endl;
}
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;
}
}
switch(people.other_contact_case())
{
case PeopleInfo::OtherContactCase::kQq:
cout << "qq号:" << people.qq() <<endl;
break;
case PeopleInfo::OtherContactCase::kWechat:
cout << "微信号:" << people.wechat() << endl;
break;
}
if(people.remark_size())
{
cout << "备注信息:" << endl;
}
for(auto it = people.remark().cbegin(); it != people.remark().cend(); ++it)
{
cout << " " << it->first << ": " << it->second << endl;
}
}
}
int main()
{
Contacts contacts;
fstream input("contacts.bin", ios::in | ios::binary);
if(!contacts.ParseFromIstream(&input))
{
cerr << "Failed to parse contacts." << endl;
return -1;
}
PrintContacts(contacts);
input.close();
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
运行结果
./write
./read
标签:PeopleInfo,string,contacts,protobuf,proto,C++,通讯录,类型,cout From: https://blog.csdn.net/tang20030306/article/details/141093438新人创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看。