Protobuf——基本使用

什么是Protobuf

Protobuf是 Google的⼀种语⾔⽆关、平台⽆关、可扩展的序列化结构数据的⽅法,它可⽤于(数据)通信协议、数据存储等。
Protobuf类比于XML,是一种灵活,高效,自动化机制的结构数据序列化方法,但是比XML更小,更快,更灵活。(可以自己选择定义不同的数据结构)

为什么需要Protobuf

序列化和反序列化

  • 序列化:把对象转换为字节序列的过程
  • 反序列化:将字节序列的恢复为对象的过程。

什么情况下需要序列化

  • 存储数据:当你想把内存的对象状态保存到一个文件中或者存储到一个数据中。
  • 网络传输: 网络传输中,都是以二进制来传输的,我们无法直接传递对象,所以都需要先序列化。

Protobuf的特点

  • 语言无关、平台无关: protobuf支持java、c++、python等语言,支持多平台
  • 高效:比XML更小、更快、更简单
  • 扩展性好、兼容性好:可以更新数据结构,不破坏原有的旧程序。(例如兼容老版本)

Protobuf的使用特点

在这里插入图片描述

  • 编写.proto文件
  • 然后编译出.pb.h .pb.cc文件,调用其中的接口就可以完成序列化和反序列化了

proto3语法
Protocol Buffers 语⾔版本3,简称proto3,是.proto文件的最新版本。
它简化了protocol Buffers语言,它允许你使用C++、Java等多种语言来生成protocol buffer的代码。

package声明符
它其实很简单,就是命名空间,用来防止我们定义发生冲突

定义消息
为什么要定义消息呢?

  • 要知道,在网络传输中,我们需要使用成熟的协议或者自定义协议来处理我们的消息,所以protobuf就是以message的方式来支持我们可以自己来定制协议字段的。
syntax = "proto3"; 			//指定proto文件版本
package contacts;			//package声明符
message 消息类型名{}

有了以上的认识后,下面我会以一个通讯录的例子来讲解一下protobuf的使用

首先为contacts.proto定义联系人的字段

syntax = "proto3";package contacts;// 定义联系⼈消息 message PeopleInfo {
//	这是消息字段格式 	字段类型 字段名 = 字段唯⼀编号string name = 1; int32 age = 2;
}

但是注意一下,字段的唯一编号的范围:

  • 1-536,870,911(2^19-1),其中1900-19999不可用

1900-19999不可用是因为:在protobuf中,对这些字段进行了预留。

编译contacts.proto文件

protoc [--proto_path=IMPORT_PATH] --cpp_out=DST_DIR path/to/file.proto
protoc 是 Protocol Buffer 提供的命令⾏编译⼯具。--proto_path 指定 被编译的.proto⽂件所在⽬录,可多次指定。可简写成 -I 
IMPORT_PATH 。如不指定该参数,则在当前⽬录进⾏搜索。当某个.proto ⽂件 import 其他 
.proto ⽂件时,或需要编译的 .proto ⽂件不在当前⽬录下,这时就要⽤-I来指定搜索⽬录。--cpp_out= 指编译后的⽂件为 C++ ⽂件。OUT_DIR 编译后⽣成⽂件的⽬标路径。path/to/file.proto 要编译的.proto⽂件。

所以,我们直接编译protoc --cpp_out=. contacts.proto,生成.pb.*文件了。

其中.pb.h是存放类的声明的,而.pb.cc是存放类的实现的。 下面是一部分代码。

class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message {public:using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;void CopyFrom(const PeopleInfo& from);using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;void MergeFrom( const PeopleInfo& from) {PeopleInfo::MergeImpl(*this, from);}static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {return "PeopleInfo";}// string name = 1;void clear_name();const std::string& name() const;template <typename ArgT0 = const std::string&, typename... ArgT>void set_name(ArgT0&& arg0, ArgT... args);std::string* mutable_name();PROTOBUF_NODISCARD std::string* release_name();void set_allocated_name(std::string* name);// int32 age = 2;void clear_age();int32_t age() const;void set_age(int32_t value);
};class MessageLite {
public://序列化: bool SerializeToOstream(ostream* output) const; // 将序列化后数据写⼊⽂件流 bool SerializeToArray(void *data, int size) const;bool SerializeToString(string* output) const;//反序列化: bool ParseFromIstream(istream* input); // 从流中读取数据,再进⾏反序列化动作 bool ParseFromArray(const void* data, int size);
bool ParseFromString(const string& data);
};

以上的序列化方法没有本质的区别,只是序列化后输出的格式不同,可以根据不同的使用场景来使用。但是要注意一点:序列化的结果是二进制的,不是文本格式。

Protobuf数据类型

  • 枚举类型: 在proto文件中,第一个常量值必须是0,同时proto也会将第一个枚举常量作为enum字段的默认值
enum{XX = 0;YY = 1;
}

在这里插入图片描述

  • repeaded类型
    消息中可以包含字段任意多次(包含0次), 可以理解为一个数组
syntax = "proto3";
package contacts;// 地址
message Address{string home_address = 1; // 家庭地址string unit_address = 2; // 单位地址
}// 联系人
message PeopleInfo {string name = 1; // 姓名int32 age = 2; // 年龄message Phone {string number = 1; // 电话号码enum PhoneType {MP = 0; // 移动电话TEL = 1; // 固定电话}PhoneType type = 2; // 类型}repeated Phone phone = 3; // 电话
}
  • oneof类型
    如果消息中有很多可选字段,只有一个使用,就可以用oneof。也能有节约内存的效果

```bash
syntax = "proto3";
package contacts;// 地址
message Address{string home_address = 1; // 家庭地址string unit_address = 2; // 单位地址
}// 联系人
message PeopleInfo {string name = 1; // 姓名int32 age = 2; // 年龄message Phone {string number = 1; // 电话号码enum PhoneType {MP = 0; // 移动电话TEL = 1; // 固定电话}PhoneType type = 2; // 类型}repeated Phone phone = 3; // 电话oneof other_contact { // 其他联系⽅式:多选⼀string qq = 4;string weixin = 5;}
}
  • map类型
    语法支持创建一个关联映射字段,格式如下

map<key_type, value_type> map_field = N;

  • key_type 是除了float和bytes类型以外的任意标量类型。value_type 可以是任意类型。
  • map字段不可以⽤repeated修饰
  • map中存⼊的元素是⽆序的
syntax = "proto3";
package contacts;// 地址
message Address{string home_address = 1; // 家庭地址string unit_address = 2; // 单位地址
}// 联系人
message PeopleInfo {string name = 1; // 姓名int32 age = 2; // 年龄message Phone {string number = 1; // 电话号码enum PhoneType {MP = 0; // 移动电话TEL = 1; // 固定电话}PhoneType type = 2; // 类型}repeated Phone phone = 3; // 电话oneof other_contact { // 其他联系方式:多选string qq = 4;string weixin = 5;}map<string,string> remark = 6; //备注
}

所以,通讯录最终版本如下

contact.proto文件syntax = "proto3";package contacts2;import "google/protobuf/any.proto";message Address{string home_address = 1; //家庭地址string unit_address = 2; //单位地址
}message PeopleInfo{string name = 1;int32 age = 2;message Phone{string phone = 1;enum PhoneType{MP = 0;// 移动电话TEL = 1; //固定电话}PhoneType type = 2;}repeated Phone phone = 3;google.protobuf.Any data = 4;//可选字段中的字段编号,不能与可选字段的编号冲突。//不能在oneof中使用repeated字段。//如果在oneof中设置了多个,那么只会保留最后一次设置的成员,之前设置的oneof成员会自动清除oneof other_contact{string qq = 5;string wechat = 6;}map<string,string> remark = 7;}message Contacts{repeated PeopleInfo contacts = 1;
}
read.cc#include <iostream>
#include <fstream>
#include "contacts.pb.h"using namespace std;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.phone();cout << "   (" << phone.PhoneType_Name(phone.type()) << ")" << endl;}// 使⽤ Is() ⽅法可以⽤来判断存放的消息类型是否为 typename T。if (people.has_data() && people.data().Is<contacts2::Address>()){contacts2::Address address;//使⽤ UnpackTo() ⽅法可以将 Any 类型转回之前设置的任意消息类型。//将people中的any类型取出来转换为其他类型 并放入address中people.data().UnpackTo(&address);if (!address.home_address().empty()){cout << "联系人家庭地址:" << address.home_address() << endl;}else if (!address.unit_address().empty()){cout << "联系人单位地址:" << address.unit_address() << endl;}}//other_contact_case 可以获取到当前设置了的字段switch(people.other_contact_case()){case contacts2::PeopleInfo::OtherContactCase::kQq:cout << "联系人qq号" << people.qq() <<endl;case contacts2::PeopleInfo::OtherContactCase::kWechat:cout << "联系人微信号" << people.wechat() <<endl;case contacts2::PeopleInfo::OtherContactCase::OTHER_CONTACT_NOT_SET: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()
{contacts2::Contacts contacts;fstream input("contacts.bin", ios::in | ios::binary);if (!contacts.ParseFromIstream(&input)){cerr << "parse error!" << endl;input.close();return -1;}PrintContacts(contacts);return 0;
}

在这里插入图片描述

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

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

相关文章

【人工智能】ChatGPT多模型感知态识别

目录 ChatGPT辅助细化知识增强&#xff01;一、研究背景二、模型结构和代码任务流程一&#xff1a;启发式生成 三、数据集介绍三、性能展示实现过程运行过程训练过程 ChatGPT辅助细化知识增强&#xff01; 多模态命名实体识别&#xff08;MNER&#xff09;最近引起了广泛关注。…

Spring源码(十二):Spring MVC之Spring Boot

本篇将详细讨论Spring Boot 的启动/加载、处理请求的具体流程。我们先从一个简单的Spring Boot项目日志开始分析&#xff08;这里假设读者已经仔细阅读完了前面的文章&#xff0c;且对Spring源码有一定深度的了解&#xff0c;否则会看得一脸懵逼&#xff09;。 本文为2024重置…

c语言-教零基础/新手入门

C 简介 一提到语言这个词语&#xff0c;自然会想到的是像英语&#xff0c;汉语等这样的自然语言&#xff0c;因为它是人和人交换信息不可缺少的工具。而今天计算机普遍了我们生活的每一个角落&#xff0c;除了人和人的相互交流之外&#xff0c;我们必须和计算机交流。就像人类…

【安装配置教程】二、VMware安装并配置ubuntu22.04

一、准备&#xff1a; 虚拟机安装ubuntu&#xff0c;首先要先找到一个镜像&#xff0c;可以去ubuntu官方下载一个&#xff0c;地址&#xff1a;下载Ubuntu桌面系统 | Ubuntu&#xff0c;下载好iso的镜像文件后保存好&#xff0c;接下来打开VMware。 二、安装&#xff…

立体视觉的核心技术:视差计算与图像校正详解

立体视觉的核心技术&#xff1a;视差计算与图像校正详解 在立体视觉中&#xff0c;通过双目相机&#xff08;即左右两台相机&#xff09;的不同视角捕获的图像&#xff0c;结合几何关系&#xff0c;我们可以推算出场景中物体的深度。本文将深入讲解如何基于视差&#xff08;di…

深度学习基础练习:从pytorch API出发复现LSTM与LSTMP

2024/11/5-2024/11/7&#xff1a; 前置知识&#xff1a; [译] 理解 LSTM(Long Short-Term Memory, LSTM) 网络 - wangduo - 博客园 【官方双语】LSTM&#xff08;长短期记忆神经网络&#xff09;StatQuest_哔哩哔哩_bilibili 大部分思路来自于&#xff1a; PyTorch LSTM和LSTMP…

scala学习记录,Set,Map

set&#xff1a;集合&#xff0c;表示没有重复元素的集合&#xff0c;特点&#xff1a;唯一 语法格式&#xff1a;val 变量名 Set [类型]&#xff08;元素1&#xff0c;元素2...&#xff09; 可变不可变 可变&#xff08;mutable&#xff09;可对元素进行添加&#xff0c;删…

Docker了解

Docker是一种容器化技术&#xff0c;它可以将应用程序和其依赖项打包到一个独立的、可移植的容器中&#xff0c;以便在不同的环境中运行。Docker基于Linux操作系统的容器化技术&#xff0c;可以提供更轻量、更快速、更灵活、更一致的应用部署和管理方式。 Docker的基本概念包括…

‘conda‘ 不是内部或外部命令,也不是可运行的程序或批处理文件,Miniconda

下载了conda&#xff0c;但是在cmd里执行conda --version会显示’conda’ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。 原因是环境变量里没有添加conda&#xff0c;无法识别路径。 需要在系统环境变量里添加如下路径&#xff1a; 保存之后重新打开cmd&am…

《TCP/IP网络编程》学习笔记 | Chapter 4:基于TCP的服务器端/客户端(1)

《TCP/IP网络编程》学习笔记 | Chapter 4&#xff1a;基于TCP的服务器端/客户端&#xff08;1&#xff09; 《TCP/IP网络编程》学习笔记 | Chapter 4&#xff1a;基于TCP的服务器端/客户端&#xff08;1&#xff09;理解TCP和UDPTCP/IP协议栈TCP/IP协议的诞生背景链路层网络层T…

【C语言】。末尼

用js写一个冒泡排序 当然可以。在JavaScript中&#xff0c;你可以使用递归的方式来实现冒泡排序算法。这是一个简单的示例&#xff1a; function bubbleSort(arr) { let len arr.length; if(len < 1){ return arr; // 如果数组只有一个元素或为空&#xf…

深度学习笔记12

1.神经网络的代价函数 神经网络可同时用于解决分类问题和回归问题&#xff0c;对于不同的问题会在输出层后&#xff0c;加上不同的变换函数。一般来说&#xff0c;回归问题使用恒等函数,分类问题使用sigmoid或softmax函数。而不同的变换函数&#xff0c;也对应不同的代价函数。…

RabbitMQ队列详细属性(重要)

RabbitMQ队列详细属性 1、队列的属性介绍1.1、Type&#xff1a;队列类型1.2、Name&#xff1a;队列名称1.3、Durability&#xff1a;声明队列是否持久化1.4、Auto delete&#xff1a; 是否自动删除1.5、Exclusive&#xff1a;1.6、Arguments&#xff1a;队列的其他属性&#xf…

json即json5新特性,idea使用json5,fastjson、gson、jackson对json5支持

文章目录 1.新特性1.1.JSON&#xff06;JSON5官网2.示例2.1. IntelliJ IDEA2.1.1.支持.json5文件2.1.2.md支持json5代码块 2.9. 示例源码 1.新特性 【通用】 注释尾随逗号key无需引号&#xff08;或单引号&#xff09; 【字符串】 字符串可以用单引号引起来。字符串可以通过转…

【NOIP普及组】摆花

【NOIP普及组】摆花 C语言代码C 代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 小明的花店新开张&#xff0c;为了吸引顾客&#xff0c;他想在花店的门口摆上一排花&#xff0c;共 m 盆。通过调 查顾客的喜好&am…

pdf转excel;pdf中表格提取

一、问题描述 在工作中或多或少会遇到&#xff1a;需要将某份pdf中的表格数据提取出来&#xff0c;以便能够“修改使用”数据 可将pdf中的表格提取出来&#xff0c;解决办法还有点复杂 尤其涉及“pdf中表格不是标准的单元格”的时候&#xff0c;提取数据到excel不太容易 比…

Qt中 QWidget 和 QMainWindow 区别

QWidget 用来构建简单窗口 QMainWindow 用来构建更复杂的窗口&#xff0c;QMainWindow 继承自QWidget&#xff0c;在QWidget 的基础上提供了菜单栏、工具栏、状态栏等功能 菜单栏&#xff08;QMenuBar&#xff09;工具栏&#xff08;QToolBar&#xff09;状态栏&#xff08;Q…

《深入浅出Apache Spark》系列③:Spark SQL解析层优化策略与案例解析

导读&#xff1a;本系列是Spark系列分享的第三期。第一期分享了Spark Core的一些基本原理和一些基本概念&#xff0c;包括一些核心组件。Spark的所有组件都围绕Spark Core来运转&#xff0c;其中最活跃的一个上层组件是Spark SQL。第二期分享则专门介绍了Spark SQL的基本架构和…

安全的时钟启动

Note&#xff1a;文章内容以 Xilinx 系列 FPGA 进行讲解 1、什么是安全启动时钟 通常情况下&#xff0c;在MMCM/PLL的LOCKED信号抬高之后&#xff08;由0变为1&#xff09;&#xff0c;MMCM/PLL就处于锁定状态&#xff0c;输出时钟已保持稳定。但在此之前&#xff0c;输出时钟会…

【mongodb】数据库的安装及连接初始化简明手册

NoSQL(NoSQL Not Only SQL )&#xff0c;意即"不仅仅是SQL"。 在现代的计算系统上每天网络上都会产生庞大的数据量。这些数据有很大一部分是由关系数据库管理系统&#xff08;RDBMS&#xff09;来处理。 通过应用实践证明&#xff0c;关系模型是非常适合于客户服务器…