【protobuf】ProtoBuf——proto3语法详解、字段规则、消息类型的定义与使用、通讯录的写入和读取功能实现

文章目录

  • ProtoBuf
    • 5. proto3语法详解
      • 5.1 字段规则
      • 5.2 消息类型的定义与使用

ProtoBuf

在这里插入图片描述

  

5. proto3语法详解

  在语法详解部分,依旧通过项目推进的方式开展教学。此部分会对通讯录多次升级,用 2.x 表示升级的版本,最终将完成以下内容的升级:

  (1)不再打印联系人的序列化结果,而是把通讯录序列化后写入文件。

  (2)从文件中解析出通讯录,并予以打印。

  (3)新增联系人属性,涵盖:姓名、年龄、电话信息、地址、其他联系方式、备注。

  

5.1 字段规则

  消息的字段能够运用以下几种规则加以修饰:

  singular :消息中能够包含该字段零次或者一次(不超过一次)。在 proto3 语法里,字段默认采用该规则。

  我们在 Protobuf 定义的消息结构中,对于被标记为 singular 规则的字段,在一条消息里,这个字段可以不出现(即零次),也可以出现一次,但最多只能出现一次。

  

  repeated :消息中能够包含该字段任意多次(包含零次),其中重复值的顺序会得以保留。

  这意味着在我们定义的消息结构中,对于被标记为 “repeated” 规则的字段,在一条消息里,它可以出现零次、一次或者多次,没有数量上的限制。可以理解为定义了一个数组。

  

   接下来我们就可以使用repeated字段在通讯录中添加我们的电话字段了:

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

  
  protoc编译文件:

protoc --cpp_out=. contacts.proto

  
  查看contacts.pb.h文件,这就是自动生成的有关repeated的函数:

在这里插入图片描述

  
  有关repeated类型的函数,下面以phone_numbers为例(自动生成的函数名和我们设置的字段名相关):

  
  获取相关函数:

  int phone_numbers_size() const;:这个函数用于获取 phone_numbers 字段中元素的数量。

  const std::string& phone_numbers(int index) const;:通过指定索引 index,获取 phone_numbers 中对应位置的元素,返回的是常量引用,意味着不能通过这个返回值修改元素。

  
  清空相关函数:

  void clear_phone_numbers();:该函数用于清空 phone_numbers 字段中的所有元素。

  
  修改相关函数:

  std::string* mutable_phone_numbers(int index);:通过索引获取 phone_numbers 中对应位置元素的指针,可通过这个指针修改对应位置的元素。

  void set_phone_numbers(int index, const std::string& value);:通过指定索引和一个字符串常量值,设置 phone_numbers 中对应位置的元素。

  void set_phone_numbers(int index, std::string&& value);:通过指定索引和一个右值引用的字符串,设置对应位置的元素。

  void set_phone_numbers(int index, const char* value);:通过指定索引和一个字符指针,设置对应位置的元素。

  void set_phone_numbers(int index, const char* value, size_t size);:通过指定索引、字符指针以及字符数量,设置对应位置的元素。

  
  添加相关函数:

  std::string* add_phone_numbers();:用于向 phone_numbers 字段添加一个新元素,并返回指向新添加元素的指针,以便进行后续修改。

  void add_phone_numbers(const std::string& value);:向 phone_numbers 字段添加一个字符串常量值。

  void add_phone_numbers(std::string&& value);:向 phone_numbers 字段添加一个右值引用的字符串。

  void add_phone_numbers(const char* value);:向 phone_numbers 字段添加一个通过字符指针表示的字符串。

  void add_phone_numbers(const char* value, size_t size);:向 phone_numbers 字段添加一个通过字符指针和字符数量表示的字符串。

  
  整体获取 / 修改相关函数:

  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField < std::string> & phone_numbers() const;:获取整个 phone_numbers 字段的常量引用。

  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField < std::string> * mutable_phone_numbers();:获取整个 phone_numbers 字段的可修改指针。

  

5.2 消息类型的定义与使用

  在单个 .proto 文件中可以定义多个消息体,且支持定义嵌套类型的消息(任意多层)。每个消息体的字段编号可以重复。

  
  单个 .proto 文件嵌套写法:

syntax = "proto3";
package contacts;// 定义联系⼈消息
message PeopleInfo {string name = 1; // 姓名int32 age = 2; // 年龄// 可以在消息字段中定义消息字段message Phone{ string number = 1;   }
}

  
  单个 .proto 文件非嵌套写法:

syntax = "proto3";
package contacts;message Phone{ string number = 1;   
}// 定义联系⼈消息
message PeopleInfo {string name = 1; // 姓名int32 age = 2; // 年龄
}

  
  多个 .proto 文件导入头文件写法:

  导入其它.proto文件:import path/xxx.proto,引入的文件声明了package,使用其类型时需要用 命名空间 . 消息类型 的格式。

// 这个是a.proto文件
syntax = "proto3";
package A;message Phone{ string number = 1;   
}
// 这个是contacts.proto文件
syntax = "proto3";
package contacts;import "a.proto";// 定义联系⼈消息
message PeopleInfo {string name = 1; // 姓名int32 age = 2; // 年龄// 引用包A,创建Phone类型消息字段,定义为phoneA.Phone phone = 3; // 使用类外定义
}

  
  我们的通讯录程序又得以再次的优化了,在 PeopleInfo 消息中:name:字符串类型,代表姓名。age:32 位整数类型,代表年龄。

  嵌套的 Phone 消息,其中包含 number 字段,为字符串类型,代表电话号码。phone:Phone 类型的重复字段,代表电话信息。

  在 Contacts 消息中:contacts:PeopleInfo 类型的重复字段,代表通讯录中的联系人信息。

syntax = "proto3";
package contacts;// 联系⼈
message PeopleInfo {string name = 1; // 姓名int32 age = 2; // 年龄message Phone {string number = 1; // 电话号码}repeated Phone phone = 3; // 电话
}// 通讯录
message Contacts {repeated PeopleInfo contacts = 1;
}

  
  编译再次生成我们所需要的 .h 和 .cc文件:

在这里插入图片描述

  

  通讯录 2.0 的写入实现:

#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 << "-----------添加联系⼈成功-----------" << endl;
}int main(int argc, char *argv[])
{GOOGLE_PROTOBUF_VERIFY_VERSION;if (argc != 2){cerr << "Usage: " << argv[0] << " CONTACTS_FILE" << endl;return -1;}Contacts contacts;// 先读取已存在的 contactsfstream input(argv[1], ios::in | ios::binary);if (!input){cout << argv[1] << ": File not found. Creating a new file." << endl;}else if (!contacts.ParseFromIstream(&input)){cerr << "Failed to parse contacts." << endl;input.close();return -1;}// 新增⼀个联系⼈AddPeopleInfo(contacts.add_contacts());// 向磁盘⽂件写⼊新的 contactsfstream output(argv[1], 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();google::protobuf::ShutdownProtobufLibrary();return 0;
}

  

  GOOGLE_PROTOBUF_VERIFY_VERSION 宏用于验证所链接的库版本与编译的头文件是否兼容。若检测到版本不匹配,程序会中止。每个 .pb.cc 文件启动时会自动调用此宏。在使用 C++ Protocol Buffer 库前执行该宏是良好做法。

  
  在程序结束时可调用 ShutdownProtobufLibrary() 来删除 Protocol Buffer 库分配的所有全局对象。对多数程序而言这并非必要,因程序退出时操作系统会回收内存。但如果使用内存泄漏检查程序,或编写可被单个进程多次加载和卸载的库,可能需要强制 Protocol Buffers 清理所有内容。

  

  Makefile:

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

  

  接着就可以向我们指定的contacts.bin文件中写入信息了:

在这里插入图片描述
  

  查看二进制文件:

  hexdump 是 Linux 中的二进制文件查看工具,能把二进制文件转换成 ASCII、八进制、十进制、十六进制格式来查看。其中 -C 选项意味着每个字节会以十六进制形式和对应的 ASCII 字符显示。

在这里插入图片描述

  

  decode 是 ProtoBuf 中通过 protoc -h 命令可查看到的一个命令选项 --decode 。它的作用是从标准输入读取给定类型的二进制消息,并将其以文本格式输出到标准输出。而且,该消息的类型必须在 .proto 文件或导入的文件中有定义。

  具体用法如下: protoc --decode=MESSAGE_TYPE MESSAGE_TYPE所在文件
  

protoc --decode=contacts.Contacts contacts.proto < contacts.bin

在这里插入图片描述

  

  通讯录 2.0 的读取实现:

#include <iostream>
#include <fstream>
#include "contacts.pb.h"
using namespace std;
using namespace contacts;//打印联系⼈列表
void PrintfContacts(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 (const PeopleInfo_Phone &phone : people.phone()){cout << "电话" << j++ << ": " << phone.number() << endl;}}
}int main(int argc, char *argv[])
{GOOGLE_PROTOBUF_VERIFY_VERSION;if (argc != 2){cerr << "Usage: " << argv[0] << "CONTACTS_FILE" << endl;return -1;}// 以⼆进制⽅式读取 contactsContacts contacts;fstream input(argv[1], ios::in | ios::binary);if (!contacts.ParseFromIstream(&input)){cerr << "Failed to parse contacts." << endl;input.close();return -1;}// 打印 contactsPrintfContacts(contacts);input.close();google::protobuf::ShutdownProtobufLibrary();return 0;
}

  

  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

  

  我们可以读取到write写入文件的信息了:

在这里插入图片描述

            

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

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

相关文章

海康VisionMaster使用学习笔记4-快速匹配模块

快速匹配模块 快速匹配包括基本参数,特征模板,运行参数,结果显示 基本参数 可以修改图像源和模块的ROI区域. 特征模版 可以配置管理所有的模版,点击创建可以新增模版,也可以通过载入加载本地的模型 建立新模版 点击创建,可以选择当前图像或本地图像进行建模 模版存图按…

使用docker compose一键部署 Portainer

使用docker compose一键部署 Portainer Portainer 是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便地管理Docker环境&#xff0c;包括单机环境和集群环境。 1、创建安装目录 mkdir /data/partainer/ -p && cd /data/partainer2、创建docker…

uni-app 使用九宫格(uni-grid)布局组件

1、运行环境 开发工具为 HBuilder X 4.23, 操作系统为 Windows 11。Vue.js 版本为 3. 2、操作步骤 首先&#xff0c;登录 HBuilder X。然后用桌面浏览器&#xff0c;访问官网组件网址。 https://ext.dcloud.net.cn/plugin?nameuni-grid 在组件网址右上角、点击“下载插…

每日一题-贪心算法

122. 买卖股票的最佳时机 II - 力扣&#xff08;LeetCode&#xff09; 55. 跳跃游戏 - 力扣&#xff08;LeetCode&#xff09; 这个题目一开始肯定是会懵&#xff0c;就比如说一开始先跳几步&#xff0c;之后再怎么跳&#xff0c;其实我们就可以用最大范围来算就行了&#xff0…

开发笔记:uniapp+vue+微信小程序 picker +后端 省市区三级联动

写在前面 未采用: 前端放置js 或者 json文件进行 省市区三级联动 采用&#xff1a; 前端组件 后端接口实现三级联动 原因&#xff1a;首先微信小程序有大小限制&#xff0c;能省则省&#xff0c;其次&#xff1a;方便后台维护省市区数据&#xff0c;完整省市区每年更新好像…

SQL基础教程(八)SQL高级处理

※食用指南&#xff1a;文章内容为《SQL基础教程》系列学习笔记&#xff0c;该书对新手入门非常友好&#xff0c;循序渐进&#xff0c;浅显易懂&#xff0c;本人主要用来补全学习MySQL中未涉及的部分&#xff0c;便于刷题和做项目。 官方电子书&#xff1a;《SQL基础教程》第2…

Web安全:SqlMap工具

一、简介 sqlmap 是一款开源的渗透测试工具&#xff0c;可以自动化进行SQL注入的检测、利用&#xff0c;并能接管数据库服务器。它具有功能强大的检测引擎,为渗透测试人员提供了许多专业的功能并且可以进行组合&#xff0c;其中包括数据库指纹识别、数据读取和访问底层文件系统…

柔性超级电容器咋储能?生物聚合物在其中起啥作用?有啥挑战?

*本文只作阅读笔记分享* 一、引言 随着对化石燃料影响的日益关注&#xff0c;开发用于先进电化学能量存储设备的绿色和可再生材料变得至关重要。超级电容器因其出色的寿命、安全性和宽温度操作范围等优势而成为有前途的储能候选者。柔性超级电容器特别适合为轻质可穿戴电子设…

我常用的几个傻瓜式爬虫工具,收藏!

爬虫类工具主要两种&#xff0c;一种是编程语言第三方库&#xff0c;比如Python的scrapy、selenium等&#xff0c;需要有一定的代码基础&#xff0c;一种是图形化的web或桌面应用&#xff0c;比如Web Scraper、后羿采集器、八爪鱼采集器、WebHarvy等&#xff0c;接近于傻瓜式操…

qt生成一幅纯马赛克图像

由于项目需要&#xff0c;需生成一幅纯马赛克的图像作为背景&#xff0c;经过多次测试成功&#xff0c;记录下来。 方法一&#xff1a;未优化方法 1、代码&#xff1a; #include <QImage> #include <QDebug> #include <QElapsedTimer>QImage generateMosa…

MyBatis全解

目录 一&#xff0c; MyBatis 概述 1.1-介绍 MyBatis 的历史和发展 1.2-MyBatis 的特点和优势 1.3-MyBatis 与 JDBC 的对比 1.4-MyBatis 与其他 ORM 框架的对比 二&#xff0c; 快速入门 2.1-环境搭建 2.2-第一个 MyBatis 应用程序 2.3-配置文件详解 (mybatis-config.…

Pikachu-XSS漏洞之cookie值获取、钓鱼结果和键盘记录实战记录

目录 Pikachu-XSS漏洞之cookie值获取、钓鱼结果和键盘记录实战记录 一、XSS&#xff08;get型&#xff09;之cookie值获取&#xff1a; 二、xss&#xff08;post型&#xff09;之cookie值获取 三、Xss之钓鱼攻击 四、XSS获取键盘记 Pikachu-XSS漏洞之cookie值获取、钓鱼结果…

坐牢第二十七天(聊天室)

基于UDP的网络聊天室 一.项目需求&#xff1a; 1.如果有用户登录&#xff0c;其他用户可以收到这个人的登录信息 2.如果有人发送信息&#xff0c;其他用户可以收到这个人的群聊信息 3.如果有人下线&#xff0c;其他用户可以收到这个人的下线信息 4.服务器可以发送系统信息…

算法工程师第四十天(647. 回文子串 516.最长回文子序列 动态规划总结篇 )

参考文献 代码随想录 一、回文子串 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 示例 1&#xff1a; 输入&#xff1a;s "abc"…

【stm32项目】多功能智能家居室内灯光控制系统设计与实现(完整工程资料源码)

多功能智能家居室内灯光控制系统设计与实现 目录&#xff1a; 目录&#xff1a; 前言&#xff1a; 一、项目背景与目标 二、国内外研究现状&#xff1a; 2.1 国内研究现状&#xff1a; 2.2 国外研究现状&#xff1a; 2.3 发展趋势 三、硬件电路设计 3.1 总体概述 3.2 硬件连接总…

图像压缩算法

8.1 JPEG压缩 (JPEG Compression) 介绍 JPEG&#xff08;Joint Photographic Experts Group&#xff09;压缩是最常用的有损图像压缩算法之一。它通过减少图像中的冗余数据来实现高效压缩&#xff0c;特别适用于自然图像。 原理 JPEG压缩的基本步骤包括颜色空间转换、离散余…

WPF篇(18)-DataGrid数据表格控件+ComboBox下拉框控件

DataGrid数据表格控件 DataGrid是一个可以多选的数据表格控件。所以&#xff0c;它继承一个支持多选的父类——MultiSelector。 public abstract class MultiSelector : Selector {protected MultiSelector();public IList SelectedItems { get; }protected bool CanSelectMu…

Python学习day16-类与对象

这里写目录标题 类示例 成员方法self关键字 类与对象构造方法其他类内置方法&#xff08;魔术方法&#xff09;_str_符号_Lt_符号le小于等于比较eq比较运算小结 类 在Python中&#xff0c;class&#xff08;类&#xff09;是一种用于创建对象的模板或蓝图。它封装了数据&#…

基于Django的停车场车辆出入管理系统,可识别车牌图片

研究背景 随着城市化进程的加快&#xff0c;车辆数量不断增加&#xff0c;停车场的管理成为一个日益重要的课题。传统的停车场管理系统依赖人工登记和监控&#xff0c;不仅效率低下&#xff0c;而且容易出现疏漏和错误&#xff0c;难以满足现代社会对停车场管理智能化、高效化…

STM32标准库学习笔记-3.外部中断

参考教程&#xff1a;【STM32入门教程-2023版 细致讲解 中文字幕】 中断 中断含义&#xff1a;在计算机执行主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&…