【C++】protobuf的简单使用(通讯录例子)
protobuf的简单使用(通讯录例子)
- .proto文件的编写
- 保留字段
- 字段唯一编号
- protobuf的类型
- enum类型
- Any类型
- oneof类型
- map类型
- 完整通讯录代码
- .proto文件
- write文件
- read文件
- 运行结果
.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


新人创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看。
相关文章:
【C++】protobuf的简单使用(通讯录例子)
protobuf的简单使用(通讯录例子) .proto文件的编写保留字段字段唯一编号protobuf的类型enum类型Any类型oneof类型map类型完整通讯录代码.proto文件write文件read文件运行结果 .proto文件的编写 syntax用于指定protobuf的语法;package当.prot…...
Apple 智能基础语言模型
Introducing Apple’s On-Device and Server Foundation Models technical details June 10, 2024 在2024年的全球开发者大会上,苹果推出了Apple Intelligence,这是一个深度集成到iOS 18、iPadOS 18和macOS Sequoia中的个人智能系统。Apple Intelligen…...
GreptimeDB融资数百万美元; Oracle提供免费长期MySQL; 谷歌大模型支持云数据库问题洞察
重要更新 1. 开源时序数据库 GreptimeDB宣布完成数百万美元的新一轮融资。GreptimeDB是一款Rust 语言编写的时序数据库,具有分布式,开源,云原生,兼容性强等特点,帮助企业实时读写、处理和分析时序数据的同时࿰…...
Java中的抽象类与接口
1. 抽象类 1.1 抽象类概念 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的, 如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。 比如&…...
云计算概念以及与云服务的区别
目录 1.云的概念 1.1 什么是云? 1.2 云计算的类型 1.3 云计算的服务模式 1.4 云计算的优势 2.云计算和云服务的区别 2.1 定义 2.2 范围 2.3 角色 2.5 举例 2.6使用者 3.总结 1.云的概念 1.1 什么是云? “云”在计算机科学和信息技术领域通常…...
Netty技术全解析:LengthFieldBaseFrameDecoder类深度解析
❃博主首页 : 「码到三十五」 ,同名公众号 :「码到三十五」,wx号 : 「liwu0213」 ☠博主专栏 : <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 :…...
深入InnoDB核心:揭秘B+树在数据库索引中的高效应用
目录 一、索引页与数据行的紧密关联 (一)数据页的双向链表结构 (二)记录行的单向链表结构 二、未创建索引情况 (一)无索引下的单页查找过程 以主键为搜索条件 以非主键列为搜索条件 (二…...
c++(面向对象的性质:抽象,封装,继承,多态)
ctrla全选,ctrli对齐 ctrl/ 一起注释 ctrlz 退回上一步 一些基础的内容: cout:输出流对象 cin:输入流对象 输入一个i和一个j,然后输出ij的和: 值不变的原因: 值传递,a和i是…...
java基础学习笔记1
Java编程规范 命名风格 1. 【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。 反例:_name / __name / $name / name_ / name$ / name__ 2. 【强制】代码中的命名严禁使用拼音与英文混合的方式,更不允许直…...
[VBA]使用VBA在Excel中 操作 形状shape 对象
excel已关闭地图插件,对于想做 地图可视化 的,用形状来操作是一种办法,就是要自行找到合适的 地图形状,修改形状颜色等就可以用于 可视化展示不同省市销量、人口等数据。 引言 在Excel中,通过VBA(Visual Basic for Applications)可以极大地增强数据可视化和报告自动化…...
Apache POI 实现 Excel 表格下载
这里以苍穹外卖中数据导出功能为例,记录下 Apache POI 导出 Excel 表格的过程。 首先在 pom.xml 中导入相关依赖 <!-- poi 用于操作 excel 表格--> <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId&…...
大华嵌入式面试题大全及参考答案(2万字长文)
目录 在C语言中,static 关键字有哪些主要用途? static 修饰的全局变量与普通全局变量有什么区别? 为什么要在嵌入式系统中使用 static 修饰函数? 虚函数与纯虚函数了解么? strcpy 给你加结束符吗,还是要自己加? select 的作用是什么,它和 epoll 的区别? map 与…...
C语言——查漏补缺
前言 本篇博客主要记录一些C语言的遗漏点,完成查漏补缺的工作,如果读者感兴趣,可以看看下面的内容。都是一些小点,下面进入正文部分。 1. 字符汇聚 编写代码,演示多个字符从两端移动,向中间汇聚 #inclu…...
Python | Leetcode Python题解之第328题奇偶链表
题目: 题解: class Solution:def oddEvenList(self, head: ListNode) -> ListNode:if not head:return headevenHead head.nextodd, even head, evenHeadwhile even and even.next:odd.next even.nextodd odd.nexteven.next odd.nexteven even…...
吉瑞外卖笔记
1.项目整体搭建 这里用到的是springboot3mybatisplus 1.1数据库搭建 整体表搭建,这里我是直接用的老师给的数据库 1.2maven项目搭建 依赖 这两个jar包第一次用,记录一下 fastjson json处理,可将对象转化为json形式 可将对象中的属性…...
Perl套接字编程指南:构建网络通信应用
摘要 Perl是一种功能强大的脚本语言,广泛应用于系统管理、网络编程等多种场景。Perl的套接字编程能力允许开发者创建客户端和服务器端的网络应用。本文将详细介绍Perl中套接字的使用,包括基础概念、API的使用,以及构建简单客户端和服务器的示…...
达梦数据库(十) -------- mybatis-plus 整合达梦时,自动生成的 sql 语句报错
一丶【问题描述】: mybatis-plus 整合达梦时,应用系统项目的 sql 语句中包含数据库关键字,导致 mybatis-plus 自动生成的 sql 语句会报错,如下图所示: 二丶【问题解决】: 问题原因:mybatis-pl…...
停止项目大小调整,开始搜索层自动缩放!
作者:来自 Elastic Matteo Piergiovanni,John Verwolf 我们新的 serverless 产品的一个关键方面是允许用户部署和使用 Elastic,而无需管理底层项目节点。为了实现这一点,我们开发了搜索层自动扩展,这是一种根据我们将在…...
VScode的环境编译器选择
按快捷键 Ctrl Shift P 选择即可...
在Linux中通过docker安装和配置supervisor进程守护
先在Linux中安装docker,然后在docker中安装appnode,并进行docker网络端口映射。接着登录appnode面板安装supervisor。 supervisor用于守护进程,在进程意外终止后将其重启。 supervisor没有监听内部程序和自动重启的功能。 docker安装 第一…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
机器学习的数学基础:线性模型
线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...
CppCon 2015 学习:Simple, Extensible Pattern Matching in C++14
什么是 Pattern Matching(模式匹配) ❝ 模式匹配就是一种“描述式”的写法,不需要你手动判断、提取数据,而是直接描述你希望的数据结构是什么样子,系统自动判断并提取。❞ 你给的定义拆解: ✴ Instead of …...
