当前位置: 首页 > wzjs >正文

网站建设打造学院网页入口网站推广

网站建设打造学院,网页入口网站推广,石家庄最新信息,医院网站建设步骤【ProtoBuf】proto3语法(一) 文章目录 【ProtoBuf】proto3语法(一)proto3语法解析字段规则消息类型的定义与使用编写通讯录demo新增/读取联系人并进行序列化读取二进制数据并进行反序列化 proto3语法解析 在语法详解部分。这个部…

【ProtoBuf】proto3语法(一)

文章目录

  • 【ProtoBuf】proto3语法(一)
  • proto3语法解析
    • 字段规则
    • 消息类型的定义与使用
    • 编写通讯录demo
      • 新增/读取联系人并进行序列化
      • 读取二进制数据并进行反序列化


proto3语法解析

在语法详解部分。这个部分会对通讯录进⾏多次升级,使⽤ 2.x表⽰升级的版本,最终将会升级如下内容:

  • 不再打印联系⼈的序列化结果,⽽是将通讯录序列化后并 写⼊⽂件中
  • 从⽂件中将通讯录解析出来,并进⾏打印。
  • 新增联系⼈属性,共包括:姓名、年龄、电话信息、地址、其他联系⽅式、备注

字段规则

消息的字段可以⽤下⾯⼏种规则来修饰:

  • singular :消息中可以包含该字段零次或⼀次(不超过⼀次)。 proto3 语法中,字段默认使⽤该规则。
  • repeated :消息中可以包含该字段任意多次(包括零次),其中重复值的顺序会被保留。可以理解为定义了⼀个数组。

更新 contacts.proto , PeopleInfo 消息中新增 phone_numbers 字段,表⽰⼀个联系⼈有多个号码,可将其设置为 repeated,写法如下:

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

消息类型的定义与使用

在单个 .proto ⽂件中可以定义多个消息体,且⽀持定义嵌套类型的消息(任意多层)。每个消息体中的字段编号可以重复(不同作用域)。更新 contacts.proto,我们可以将 phone_number 提取出来,单独成为⼀个消息:

syntax = "proto3";
package contacts2;message Phone
{string number  = 1;
}// 定义联系人message
message PeopleInfo
{string name = 1;    // 姓名int32 age = 2;      // 年龄// repeated string phone_numbers = 3; // 手机号码repeated Phone phone = 3;
}
  • 消息类型可作为字段类型使用:
message PeopleInfo
{string name = 1;    // 姓名int32 age = 2;      // 年龄// repeated string phone_numbers = 3; // 手机号码message Phone{string number  = 1;}repeated Phone phone = 3;
}
  • 可以导入其他 .proto 文件的消息类型并使用:

Phone消息定义在phone.proto中:

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

如果想要在contacts.proto文件中使用Phone message类型字段,则:

syntax = "proto3";
package contacts2;import "Phone.proto";// 使用import 将 Phone.proto 文件导入进来// 定义联系人message
message PeopleInfo
{string name = 1;    // 姓名int32 age = 2;      // 年龄repeated phone.Phone payphone = 3;
}
  • 注意:在proto3文件中可以导入proto2消息类型并使用它们,反之亦然。

我们使用嵌套式的作为演示,PeopleInfo里的字段已经差不多了,这样每个人的信息结构也就出来了,随后我们应该定义一个message 作为通讯录,通讯录里是一个数组,数组每一个元素都是一个PeopleInfo:

syntax = "proto3";
package contacts2;// 定义联系人message
message PeopleInfo
{string name = 1;    // 姓名int32 age = 2;      // 年龄message Phone{string number = 1;}repeated Phone phone = 3;// 电话信息
}// 通讯录message
message Contacts 
{repeated PeopleInfo contacts = 1;
}

在这里插入图片描述

我们来看一下生成的cpp代码中有什么:

在这里插入图片描述

当然,我们的关注点不在这里,我们进入到一个类当中(PeopleInfo_Phone类中,这里生成的代码有报错语法提示,是因为插件原因,不用在意,实际上没有任何问题):

在这里插入图片描述

  • clear_number():对字段进行清空
  • number():省略了get前缀,实际是get方法,获取number字段
  • set_number():设置number字段

以上几种方法是基本message常用方法,而在PeopleInfo中因为声明了Phone字段为 repeated 类型,所以会多几个不同的方法:

在这里插入图片描述
这样,带有repeated声明的成员处理方法我们也就清楚了。


编写通讯录demo

新增/读取联系人并进行序列化

对通讯录序列化有三点:

  • 读取本地已存在的联系人文件(不存在则创建该文件)
#define FILE_SAVE "contacts.bin"// 文件保存的文件名int main()
{contacts2::Contacts contacts; // 定义通讯录方法// 1. 读取本地已存在的联系人文件fstream input(FILE_SAVE, ios::in | ios::binary);if(!input){cout << "contacts.bin not found, create new file!" << endl;}else if(contacts.ParseFromIstream(&input)){cerr << "parse error!" << endl;input.close();return -1;}return 0;
}

contacts2为contacts.pb.h中的命名空间,由于PB序列化之后是二进制文件,所以从文件中读取数据使用ios::binary,读取成功之后,通过contacts.pb.h中提供的解析二进制序列方法,将通讯录数据序列化为二进制文件,并保存在 contacts.bin 文件中。

在这里插入图片描述

  • 向通讯录中添加一个联系人
// 联系人信息类型为PeopleInfo类型
void AddPeopleInfo(contacts2::PeopleInfo* people)
{cout << "---------------新增联系人---------------" << endl;cout << "请输入联系人姓名: ";string name;getline(cin, name);people->set_name(name);// 调用set方法设置联系人名称cout << "请输入联系人年龄: ";int age;cin >> age;people->set_age(age);// 调用set方法设置联系人年龄cin.ignore(256, '\n');// 防止\n影响下次输入for(int i = 0;;i++){cout << "请输入联系人电话" << i + 1 << "(只输入回车完成电话新增): ";string number;getline(cin, number);if(number.empty()){break;}contacts2::PeopleInfo_Phone* phone = people->add_phone();phone->set_number(number);}cout << "-------------添加联系人成功--------------" << endl;
}int main()
{// ...// 2. 向通讯录中添加一个联系人AddPeopleInfo(contacts.add_contacts());return 0;
}

使用 cin.ignore(256, '\n') 接口,目的是为了不让’\n’影响到接下来的输入。首先来解释第二个参数,第二个参数的含义是遇到 ‘\n’ 则会停止清空缓冲区(前面的内容全部清除,包括\n)。第一个参数的含义是如果清除了256个字符后还没有遇到 ‘\n’,则也会停下来不再清楚缓冲区。

之后就进行循环输入电话号码,如果想要停止输入(输入为空),则按两次回车即可终止。而由于电话信息是一个数组,所以一个电话信息在存储之前需要先添加一个数组元素,即调用add_phone()函数,然后再调用set函数将新输入的电话号码保存到数组中。

  • 将通讯录写入到本地文件中
int main()
{// 3. 将通讯录写入到本地文件中fstream output(FILE_SAVE, ios::out | ios::trunc | ios::binary);if(!contacts.SerializeToOstream(&output)){cerr << "write error!" << endl;input.close();output.close();return -1;}cout << "write success!" << endl;input.close();output.close();return 0;
}

最后将通讯录写入到本地文件中,写入之前我们先进行序列化,依旧使用PB提供的序列化函数来进行序列化:

在这里插入图片描述

编写makefile对通讯录项目做编译:

write:write.cc contacts.pb.ccg++ -o $@ $^ -std=c++11 -lprotobuf.PHONY:clean
clean:rm -f write 
#include <iostream>
#include <fstream>
#include "contacts.pb.h"using namespace std;#define FILE_SAVE "contacts.bin"// 文件保存的文件名void AddPeopleInfo(contacts2::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 = 0;;i++){cout << "请输入联系人电话" << i + 1 << "(只输入回车完成电话新增): ";string number;getline(cin, number);if(number.empty()){break;}contacts2::PeopleInfo_Phone* phone = people->add_phone();phone->set_number(number);}cout << "-------------添加联系人成功--------------" << endl;
}int main()
{contacts2::Contacts contacts; // 定义通讯录方法// 1. 读取本地已存在的联系人文件fstream input(FILE_SAVE, ios::in | ios::binary);if(!input){cout << "contacts.bin not found, create new file!" << endl;}else if(contacts.ParseFromIstream(&input)){cerr << "parse error!" << endl;input.close();return -1;}// 2. 向通讯录中添加一个联系人AddPeopleInfo(contacts.add_contacts());// 3. 将通讯录写入到本地文件中fstream output(FILE_SAVE, ios::out | ios::trunc | ios::binary);if(!contacts.SerializeToOstream(&output)){cerr << "write error!" << endl;input.close();output.close();return -1;}cout << "write success!" << endl;input.close();output.close();// google::protobuf::ShutdownProtobufLibrary();return 0;
}

接下来就使用make来编译文件,然后运行程序:

在这里插入图片描述

在这里插入图片描述
我们发现生成了这个文件,并且记录着我们之前输入的数据,不过这里格式不对,因为是序列化为了二进制文件,而我们不能直接读取二进制文件,我们可以通过 hexdump 指令来查看二进制序列:

  • hexdump :用来查看二进制序列的命令,有不同的选项可以将二进制序列以十六进制形式进行输出。
  • C选项: 以标准格式显示包含地址和 ASCII 值的内容。每一行显示 16 字节的十六进制数字以及对应的 ASCII 字符。
  • n <数值>选项: 仅输出前 <数值> 字节。
  • v选项: 显示所有字节,包括重复的字节。

在这里插入图片描述

当然,如果你观看别人用PB写的C++程序,可能会看到这样一句宏定义在main函数开头:

GOOGLE_PROTOBUF_VERIFY_VERSION;
  • GOOGLE_PROTOBUF_VERIFY_VERSION : 验证没有意外链接到与编译的头⽂件不兼容的库版本。如果检测到版本不匹配,程序将中⽌。注意,每个 .pb.cc ⽂件在启动时都会⾃动调⽤此宏。在使⽤ C++ Protocol Buffer 库之前执⾏此宏是⼀种很好的做法,但不是绝对必要的。

用人话来说就是可能使用PB版本不匹配,而版本不匹配是会报错的,所以在启动程序之前添加这个宏可以用来检测使用的PB版本是否一致。

而我们在结束时注释了一个函数:

google::protobuf::ShutdownProtobufLibrary();
  • google::protobuf::ShutdownProtobufLibrary() : 在程序结束时调⽤这个接口,是为了删除 Protocol Buffer 库分配的所有全局对象。对于⼤多数程序来说这是不必要的,因为该过程⽆论如何都要退出,并且操作系统将负责回收其所有内存。但是,如果你使⽤了内存泄漏检查程序,该程序需要释放每个最后对象,或者你正在编写可以由单个进程多次加载和卸载的库,那么你可能希望强制使⽤ Protocol Buffers 来清理所有内容。

读取二进制数据并进行反序列化

我们新创建一个read.cc文件用来从contacts.bin文件中进行读取二进制数据并进行反序列化解析,将makefile文件更新:

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

接下来对本地通讯录的操作有两步:

  • 读取本地已经存在的通讯录文件
#define FILE_READ "contacts.bin"// 文件保存的文件名int main()
{contacts2::Contacts contacts; // 定义通讯录对象// 1. 读取本地已存在的通讯录文件fstream input(FILE_READ, ios::in | ios::binary);if(!contacts.ParseFromIstream(&input)){cerr << "parse error!" << endl;input.close();return -1;}return 0;
}

来解析通讯录中的消息,与write.cc开始相同

  • 打印通讯录列表
void PrintContacts(contacts2::Contacts& contacts)
{for(int i = 0; i < contacts.contacts_size(); ++i){cout << "-----------------联系人" << i + 1 << "-----------------" << endl;const contacts2::PeopleInfo& people = contacts.contacts(i);cout << "联系人姓名: " << people.name() << endl;cout << "联系人年龄: " << people.age() << endl;for(int j = 0; j < people.phone_size(); ++j){const contacts2::PeopleInfo_Phone& phone = people.phone(j);cout << "联系人电话" << j + 1 << " : " << phone.number() << endl;}cout << "-----------------通讯录尾-----------------" << endl;}
}int main()
{// ...// 2. 打印通讯录列表PrintContacts(contacts);return 0;
}

由于通讯录message中只有一个PeopleInfo字段并且是数组类型,所以我们直接使用contacts()来获取数组中的元素值,即每一个联系人的基本信息。然后打印出联系人的姓名和年龄信息。

而每个人的手机号信息都是一个数组,所以我们需要对每一个联系人的手机号数组进行遍历,将所有手机号给打印出来,因为phone也是一个数组,所以可以直接调用 people.phone() 来获取手机号数组。

在这里插入图片描述


http://www.dtcms.com/wzjs/263060.html

相关文章:

  • h1z1注册网站网络营销推广方式包括哪些
  • 广州外贸网站开发宁波seo优化
  • 个人网站的重要性排名点击工具
  • 做本地b2b网站网页设计培训学校
  • 医疗网站建设案例搜索引擎平台排名
  • 长春到四平谷歌外贸seo
  • 网站要怎么运营网络营销的特征
  • 电商网站排行榜百度指数数据分析平台入口
  • wap网站技术营销模式100个经典案例
  • 小说网站怎么做不违法什么是整合营销并举例说明
  • 网站可以有二维码吗网站如何进行seo
  • 网站设计论文分类号域名网
  • 东莞企业网站电话深圳网站设计实力乐云seo
  • CMS源码就可以做网站吗百度应用商店官网
  • 北京建设主管部门网站广州代运营公司有哪些
  • 网上做的比较好的竞彩网站外贸公司一般怎么找客户
  • 网站建设业务元提成互联网营销师培训
  • 什么网站做的靠枕比较有创意智能建站模板
  • html 手机网站开发百度有哪些产品
  • 哪个网站专做童装批发重庆seo网络推广平台
  • 外贸网站建设武汉客源软件哪个最好
  • 做同城特价的网站有哪些seo 0xu
  • 东莞企业网站制作出售南京seo网站优化推广
  • 黄色网站模板百度竞价推广开户费用
  • 工作细胞樱花动漫排名优化公司电话
  • 装饰公司网站规划方案阿里指数在哪里看
  • 网站建设jiage关键词seo
  • 一级a做爰片无码网站什么公司适合做seo优化
  • 建网站出现ie6免费网站推广产品
  • 用html能做企业网站吗手机百度网址大全首页