message Phone
{
string number = 1;
}
message PeopleInfo
{
string name = 1;
int32 age = 2;
Phone phone = 3;
}
message PeopleInfo
{
string name = 1;
int32 age = 2;
message Phone
{
string number = 1;
}
Phone phone = 3;
}
// 文件 Phone.proto
syntax = "proto3";
package phone;
message Phone
{
string number = 1;
}
import "Phone.proto";
message PeopleInfo
{
string name = 1;
int32 age = 2;
// 引⼊的⽂件声明了package,使⽤消息时,需要⽤ ‘命名空间.消息类型’ 格式
phone.Phone phone = 3;
}
消息的字段可以用下面几种规则来修饰:
续回上篇,继续完善contacts.proto
文件的内容
syntax = "proto3";
package contacts;
message PeopleInfo
{
string name = 1;
int32 age = 2;
// 采用内嵌式
message Phone
{
string number = 1;
}
// repeated 修饰词 修饰的变量相当于数组
repeated Phone phone = 3;
}
// 通讯录
message Contacts
{
repeated PeopleInfo contacts = 1;
}
补充上篇:
- decode
先看用法
protoc -decode=contacts.Contacts contacts.proto < contacts.bin
执行指令后,结果如下
这个编译条件可以快速查看序列化后二进制文件的内容,是一个很便捷的操作
查看二进制内容的工具还有hexdump
hexdump:是Linux下的一个二进制文件查看工具,它可以将二进制文件转换为ASCII、八进制、十进制、十六进制格式进行查看。
-C: 表示每个字节显示为16进制和相应的ASCII字符
hexdump -C 二进制文件名
eg:hexdump -C contact.bin
上面完善编写了contacts.proto
文件,现在完成写文件write.cpp
和读文件read.cpp
,write.cpp文件主要用于将通讯录信息通过protobuf的序列化,然后写入contact.bin
文件内,read.cpp文件主要用于通过protobuf的反序列化,对contact.bin
文件内容的读取。
write.cpp的编写
#include
#include
#include"contacts.pb.h"
using namespace std;
void AddPeopleInfo(contact2::PeopleInfo *people)
{
cout << "--------------新增联系⼈--------------" << endl;
cout << "请输⼊联系⼈姓名: ";
string name;
getline(cin, name);
people->set_name(name);
cout << "请输⼊联系⼈年龄: ";
int age;
cin >> age;
cin.ignore(256, '\n'); // 清除缓冲区的内容,大于256或遇到'\n'停止
people->set_age(age);
for(int i = 1; ;++i)
{
cout << "请输⼊联系⼈电话" << i << "(只输⼊回⻋完成电话新增): ";
string number;
getline(cin, number);
if(number.empty()) break;
people->add_phone()->set_number(number);
}
cout << "-------------添加联系⼈成功-------------" << endl;
}
int main()
{
contacts::Contacts con;
fstream in("contact.bin", ios::in | ios::binary);
if(!in)
cout << "contact.bin not exist, it is being created now" << endl;
// ParseFromIstream方法的作用: 读取in里的内容并将其反序列化保存起来
else if(!con.ParseFromIstream(&in))
{
cerr << "Fail to parse" << endl;
in.close();
return -1;
}
in.close();
// 写入通讯录信息
// 前文有提到,repeated修饰的变量相当于数组,所以
// con.add_contacts()就相当新分配了一个contacts变量
AddPeopleInfo(con.add_contacts());
fstream out("contact.bin", ios::out | ios::trunc | ios::binary);
// SerializeToOstream方法的作用: 读取out里的内容并将其序列化保存起来
if(!con.SerializeToStream(&out))
{
cerr << "Fail to serialize" << endl;
out.close();
return -1;
}
cout << "write success" << endl;
out.close();
return 0;
}
执行效果如下:
read.cpp的编写
#include
#include
#include "contacts.pb.h"
using namespace std;
void PrintfContacts(contact2::Contacts &con)
{
cout << "------------- 通讯录 -------------" << endl;
for(int i = 0; i < con.contacts_size(); ++i)
{
cout << "========= 联系人 " << i+1 << " =========" << endl;
const contact2::PeopleInfo people = con.contacts(i);
cout << "姓名:" << people.name() << endl;
cout << "年龄:" << people.age() << endl;
for(int j = 0; j < people.phone_size(); ++j)
{
auto phone = people.phone(j);
cout << "号码" << j+1 << ":" << phone.number() << endl;
}
}
cout << "-------------------------------" << endl;
}
int main()
{
contact2::Contacts con;
fstream in("contact.bin", ios::in | ios::binary);
if(!in)
cout << "contact.bin not exist, it is being created now" << endl;
else if(!con.ParseFromIstream(&in))
{
in.close();
return -1;
}
PrintfContacts(con);
in.close();
return 0;
}
执行效果如下:
补充:主要函数的使用
bool ParseFromIstream(std::istream* input);
Parse 系列的函数有很多,因为前面用了文件流提取内容,所以这里使用 Parse 系列的Istream()。
作用:读取 input 里的内容并将其反序列化保存起来
add_contacts()
因为 Contacts 里的 contacts 字段被 repeat 修饰,所以相当于数组,这个函数是专门新增一个 contacts 变量的。(ps:因为有contacts所以才是 add_contacts() ,如果是 其他 那么就会对应生成 add_其他())
bool SerializeToOstream(std::ostream* output) const;
作用: 读取 output 里的内容并将其序列化保存起来
contacts_size();
求变量个数(可以认为数组大小),注意 size 前面的名称是变量名称。
set_变量名();
代码里有出现,set_age(age),set_name(name),set_number(number) 没错,这些都是设置值的函数
以上函数均包含在 文件.pb.h
里,必要是时可以跳转进去里面学习函数使用,上面仅讲解了使用到的函数