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

news2024/11/17 1:30:04

文章目录

  • 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类型消息字段,定义为phone
    A.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;

    // 先读取已存在的 contacts
    fstream 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());
    // 向磁盘⽂件写⼊新的 contacts
    fstream 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.cc
	g++ -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;
    }
    // 以⼆进制⽅式读取 contacts
    Contacts contacts;
    fstream input(argv[1], ios::in | ios::binary);
    if (!contacts.ParseFromIstream(&input))
    {
        cerr << "Failed to parse contacts." << endl;
        input.close();
        return -1;
    }
    // 打印 contacts
    PrintfContacts(contacts);
    input.close();
    google::protobuf::ShutdownProtobufLibrary();
    return 0;
}

  

  Makefile:

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

  

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

在这里插入图片描述

            

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2 C 语言开发工具的选择、 MinGW 的安装与配置、VS Code 的安装与配置、插件推荐

目录 1 开发工具的选择 1.1 Visual Studio 1.2 Code::Block 1.3 Clion 1.4 VS Code 1.5 在线编辑工具 2 开发工具安装 2.1 安装 MinGW-w64 2.1.1 MinGW-w64 介绍 2.1.2 解压 MinGW 2.1.3 将 MinGW 添加至环境变量 2.1.4 验证安装 2.2 安装 VS Code 2.2.1 下载安装…

无刷直流电机的个人总结

电机分类 主要分为两大类&#xff1a;有刷电机&#xff0c;无刷电机 有刷电机 原理图截图来源&#xff0c;工作原理就是对转子线圈通电后产生磁性和外壳中的永磁体相互作用&#xff0c;导致转子转动。而在转子转动过程中如果不改变电流方向&#xff0c;那么磁性不变&#xf…

ROS机械臂——ROS结合OpenCV案例(含资源)

纲要 摄像头驱动 图像属性 图像压缩 ### Realsense摄像头 点云展示 ### 点云图像属性 ## 摄像头标定 摄像头标定流程 如何使用标定文件 OpenCV ROS与OpenCV的集成框架 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b0ff143b710543839325d19c7a3c04c5.png R…

【Mysql】Xtrabackup备份恢复(Mysql5.6/5.7/8.0)

目录 一、前言 二、Mysql8.0为例 1、安装Xtraback工具 1&#xff09;mysql8.0.x版本 2&#xff09;mysql5.6、5.7版本 2、备份操作 3、恢复操作 一、前言 官网下载地址&#xff1a;Software Downloads - Percona 本测试环境&#xff1a;centos_x86、mysql8.0.39、mysq…

Nginx的源码编译

一:Nginx 介绍 Nginx是免费的、开源的、高性能的HTTP和反向代理服务器、邮件代理服务器、以及TCP/UDP代理服务 器 解决C10K问题&#xff08;10K Connections&#xff09; Nginx功能&#xff1a;静态的web资源服务器html&#xff0c;图片&#xff0c;js&#xff0c;css&#xff…

【记git 重命名文件失败,和正确方法】

【背景】 想要重命名一个文件&#xff0c;并同步到远程 【过程】 1.我是直接把 “驱动增加he.c” 文件重命名为 “驱动增加播放he接口方法” &#xff0c;想着直接提交就会同步重命名git仓记录的文件名。然后就可以推送到远程仓库&#xff0c;同步重命名远程仓库的文件名。 2.然…

计算机的错误计算(六十五)

摘要 计算机的错误计算&#xff08;五十五&#xff09;展示了大数的余弦函数值的错误计算 。本节探讨另外一类数值&#xff1a; 附近数 的余弦函数的计算精度问题。 例1. 已知 计算 先用 Python计算&#xff1a; 然后在 Visual Studio 2010中用下列代码计算&#xff1a; …

c语言中比较特殊的输入函数

目录 一.getchar()函数 1.基本功能 2.使用方法 (1).读取单个字符 (2).读取多个字符&#xff08;直到遇到换行符&#xff09; (3).处理输入中的空白字符 3.返回值 4.应用场景 5.注意事项 二.fgets()函数 1.函数原型 2.工作原理 3.使用示例 (1).从标准输入读取一行…

qt笔记之qml中的TextEdit、TextInput、TextArea、TextField的区别

qt笔记之qml中的TextEdit、TextInput、TextArea、TextField的区别 code review! 文章目录 qt笔记之qml中的TextEdit、TextInput、TextArea、TextField的区别一.对比二.C环境中类似功能的控件 一.对比 TextEdit、TextInput、TextArea和TextField都是用于文本输入的组件&#…

基于PostGIS(Postgres)+Node.js实现的xyz瓦片地图服务器

背景介绍 前两天研究GeoServer发布存储在PostGIS中栅格数据&#xff0c;最终目的是想在PostGIS中存储金字塔瓦片&#xff0c;用GeoServer发布&#xff0c;但是最后经过研究不改GeoServer源码的情况下&#xff0c;好像只支持将大图tif存在PostGIS数据库中进行发布&#xff0c;金…

XSS漏洞复现(包括xssgame和三个高级xss漏洞)

文章目录 XSS GAME1、Ma Spaghet!2、Jefff3、Ugandan Knuckles4、Ricardo Milos5、Ah Thats Hawt6、Ligma7、Mafia8、Ok, Boomer 全删除属性dom破坏WW3 XSS GAME 地址&#xff1a;https://xss.pwnfunction.com/ 关卡难度层层递进&#xff0c;各个漏洞都有特点&#xff0c;值得…

【最大公约数和最小公倍数】求最大公约数和最小公倍数

输入两个正整数m和n&#xff0c;求其最大公约数和最小公倍数 使用C语言实现&#xff0c;具体代码&#xff1a; 使用辗转相除法&#xff08;也称欧几里得算法&#xff09;计算最大公约数 while(b ! 0){ temp a % b; a b; b temp; } 最小公倍数&#xff08;LCM&…

大模型高效微调工具 Hugging Face PEFT

概述 HF PEFT是一个为大型预训练模型提供高效微调方法的Python库。它通过采用训练少量提示参数或使用低秩适应等重新参数化方法&#xff0c;减少微调时训练参数的数量。本文主要介绍了PEFT库的内容、与Transformers的集成、核心知识点如AutoPeftModels、PeftConfig、PeftType和…

知识改变命运 数据结构【链表面试题】

1. 删除链表中等于给定值 val 的所有节点。 OJ链接 public ListNode removeElements(ListNode head, int val) {if (headnull) {return null;}ListNode curhead.next;ListNode prehead;while(cur!null) {if(cur.valval) {pre.nextcur.next;curcur.next;}else {precur;curcur.ne…

日拱一卒(5)——leetcode学习记录:股票购买

一、任务 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的…

【qt】跳转到另一个界面

如何在一个界面跳转到另一个界面呢&#xff1f; 1.具体步骤 1.先新建一个界面 2.选择qt设计师界面 3.选择W 4.新界面名称 5.界面设计 因为我们要实现通信&#xff0c;需要一个发送信息栏&#xff0c;一个发送按钮&#xff0c;一个清空发送栏按钮 6.实现跳转 我们可以参…

链表反转算法

链表的反转有较多的方法&#xff0c;如原地算法&#xff0c;迭代法、头插法、递归法&#xff0c;本文使用递归法和迭代法两种方式进行演示。 一、定义链表 typedef struct SinglyLinkNode {/**后继节点 */struct SinglyLinkNode* next;/** 节点数据域 */int data; } Lin…

海外代理IP网速快慢取决因素有哪些

在全球化日益加深的今天&#xff0c;海外代理IP成为许多用户进行跨境网络活动的重要工具。然而&#xff0c;用户在使用海外代理IP时&#xff0c;往往会遇到网速快慢不一的问题。本文将从多个角度探讨影响海外代理IP网速快慢的因素&#xff0c;帮助用户更好地理解并选择适合自己…

使用 prefetchComponents 进行组件预取

title: 使用 prefetchComponents 进行组件预取 date: 2024/8/17 updated: 2024/8/17 author: cmdragon excerpt: 摘要&#xff1a;本文介绍Nuxt.js中的prefetchComponents功能&#xff0c;用于预取组件以提高用户体验。通过在客户端后台下载和缓存组件&#xff0c;确保在用户…

普通人如何做文献阅读汇报?

最强小说推文——AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量https://aitools.jurilu.com/ 用AI啊&#xff01;以前啃个文献小半天&#xff0c;现在AI 2min搞定&#xff0c;从提取核心论点、参考文献到摘要、翻…