【Protobuf速成指南】Message中如何定义“数组”?

news2024/11/28 14:18:31

文章目录

  • 2.0
    • 一、字段规则
      • 1.1 规则
      • 1.2 改进
      • 1.3 消息类型作为字段类型
    • 二、repeated字段使用方式
    • 三、Contact2.0
      • 1.write.cc
      • 2.read.cc
      • 3. 查看二进制信息
        • ①hexdump
        • ② --decode选项

2.0

 本系列文章将通过对通讯录项目的不断完善,带大家由浅入深的学习Protobuf的使用。这是Contacts的2.0版本,在这篇文章中将带大家正式开始编写通讯录,并进一步学习Protobuf的语法

一、字段规则

1.1 规则

消息的每个字段可以用下面的规则进行修饰:

  • singular:消息中可以包含该字段零次或一次。proto3语法中,字段默认使用该规则 (注:出现0次的字段如果没有默认值,就不会被序列化到消息中)
  • repeated:消息中可以包含该字段任意多次(包括零次),并会按先顺序保存。可以理解为定义了一个数组

1.2 改进

我们现在改进1.0版的通讯录,增加一个规则为 repeated 的 phone_number字段

syntax = "proto3";
package contact2;

message PeopleInfo{
    string name = 1;
    int32 age = 2;
    repeated string phone = 3;  // 相当于一个 string 数组了
}

下面增加一点难度,将phone类型也定义为一个message

1.3 消息类型作为字段类型

  • Protobuf是支持嵌套定义message的

    message PeopleInfo{
        string name = 1;
        int32 age = 2;
        message Phone{   // 在不同的message中唯一编号是可以重复的
            string number = 1;
            string tyepe = 2;
        }
        repeated Phone phone = 3;
    }
    
  • import 其他proto文件中定义的message类型

    // phone.proto中的内容
    syntax = "proto3";
    package phone;
    
    message Phone{
        string number = 1;
        string tyepe = 2;
    }
    
    // contact2.proto
    syntax = "proto3";
    package contact2;
    
    import "phone.proto";
    
    message PeopleInfo{
        string name = 1;
        int32 age = 2;
        // phone.xxx 表示在phonen包中
        // 如果 Phone 没有定义在任何包内,则可以省略
        repeated phone.Phone phone = 3; 
    }
    

 使用Vscode的时候,你可能会看到如下的报错,这是Vscode插件的问题,我们语法没有出错,我们可以正常编译没有问题。

image-20230606152343975

【注】:proto3可以导入proto2的消息类型并使用它,反之亦然

最终 contact.proto 文件内容如下:

syntax = "proto3";
package contact2;

message PeopleInfo{
    string name = 1;
    int32 age = 2;
    message Phone{   
        string number = 1;
        string tyepe = 2;
    }
    repeated Phone phone = 3;
}

message Contact{
    repeated PeopleInfo contact = 1;
}

二、repeated字段使用方式

编译后我们发现生成了两个类,这也是符合我们预期的image-20230606153257356

 由于Phone消息是嵌套定义在PeopleInfo消息中的,因此生成的class名前面有一个PeopleInfo_

编译后,编译器为 repeated phone 额外提供了这些接口,部分如下:

inline int PeopleInfo::phone_size() const {...}// 返回数组元素个数

inline ::contact2::PeopleInfo_Phone* PeopleInfo::mutable_phone(int index) {...}        // 相当于返回 &phone[index]

inline const ::contact2::PeopleInfo_Phone& PeopleInfo::phone(int index) const {...}  // 相当于 phone[index]

inline ::contact2::PeopleInfo_Phone* PeopleInfo::add_phone() {}
// 添加一个新的Phone对象到PeopleInfo中的phones数组中,并返回一个可变的(mutable)指向新添加的对象的指针。我们可以通过该指针修改该新对象的内容

三、Contact2.0

🎯[2.0版升级目标]:

  • 不再打印联系人的序列化结果,而是将通讯录序列化后并写入文件中。
  • 从文件中将通讯录解析出来,并进行打印。
  • 新增联系人属性例如:姓名、年龄、电话信息、地址、联系方式等。

1.write.cc

#include <iostream>
#include <fstream>
#include "contact.pb.h"

using namespace std;

void AddPeopleInfo(contact2::PeopleInfo* p){
    cout << "----------新增联系人----------" << endl;

    cout << "请输入联系人姓名: ";
    string name;
    getline(cin, name);
    p->set_name(name);

    cout << "请输入联系人年龄: ";
    int age;
    cin >> age;
    p->set_age(age);
    // 一直清空,直到读到 \n(\n也会被清除),或者清空,直到清空了256个字符
    cin.ignore(256, '\n'); 

    // 说明:cin输入后,换行符还会被留在缓冲区中,而getline读到\n就停了
    // 所以需要使用 cin.ignore 清空缓冲区从而避免对之后的输入产生影响
    for(int i = 0;; i++){
        cout << "请输入联系人电话"  << i + 1 << "(只输入回车则结束): ";
        string number;
        getline(cin, number);
        if(number.empty()) break;
        contact2::PeopleInfo_Phone* phone = p->add_phone();
        phone->set_number(number);

        cout << "请输入该电话类型: ";
        string type;
        getline(cin, type);
        phone->set_tyepe(type);
    }

    cout << "-----------添加成功-----------" << endl;
}

int main(int argc, char* argv[]){
    if(argc != 2){
        cerr << "use: ./write filename" << endl;
        exit(1);
    }

    contact2::Contact contact;
    // 1. 读取通讯录中的原始数据
    // 二进制的方式读取
    fstream input(argv[1], ios::in | ios::binary);  
    if(!input){
        cerr << argv[1] << " file not find. Creating a new file" << endl;
    } // 从输出流中反序列化
    else if(!contact.ParseFromIstream(&input)){    
        cerr << "parser original file error" << endl;
        exit(2); 
    }
    
    // 2. 新增联系人
    AddPeopleInfo(contact.add_contact());

    // 3. 写入文件中
    fstream output(argv[1], ios::out | ios::trunc | ios::binary);
    if(!output || !contact.SerializePartialToOstream(&output)){
        cerr << "write error" << endl;
        exit(3);
    }
    cout << "write success" << endl;
    input.close();
    output.close();
}

image-20230606165403671

2.read.cc

#include <iostream>
#include <fstream>
#include "contact.pb.h"

using namespace std;

void PrintContact(contact2::Contact& contact){
    for(int i = 0; i < contact.contact_size(); i++){ 
        const contact2::PeopleInfo people = contact.contact(i);
        cout << "-------------联系人" << i + 1 << "-------------" << endl;
        cout << "name: " << people.name() << endl;
        cout << "age: " << people.age() << endl;;
        int j = 1;
        for(const auto& phone : people.phone()){
            cout << "number" << j++ << ": " << phone.number()
                 << " type: " << phone.type() << endl;
        }
    }
}

int main(int argc, char* argv[])
{
    if(argc != 2){
        cerr << "use: ./read file" << endl;
        exit(1);
    }

    contact2::Contact contact;
    // 1. 读取通讯录中的原始数据
    fstream input(argv[1], ios::in | ios::binary); // 二进制的方式读取
    if(!input){
        cerr << argv[1] << " file not find. exit" << endl;
        exit(2);
    } else if(!contact.ParseFromIstream(&input)){ // 从输出流中反序列化
        cerr << "parser original file error" << endl;
        exit(2); 
    }

    PrintContact(contact);
    input.close();

    return 0;
}

image-20230606172614945

3. 查看二进制信息

①hexdump

 hexdump是一种命令行工具,它可以将二进制数据的十六进制表示以及对应的 ASCII 码字符显示出来,使您可以更容易地理解和分析这些数据。

  • -C:同时显示二进制数据的十六进制表示和 ASCII 码字符。
  • -b 参数表示显示八进制数而非十六进制数
  • -n 参数表示指定要显示的字节数

image-20230606172943738

② --decode选项

 在命令行中输入 protoc -h 便可以看到protoc支持的各种选项,我们现在重点研究 --decode 选项的使用:

image-20230606173203831

 这个选项的作用是从标准输入中读取给定类型的二进制消息,并将其以文本的格式写入到标准输出。需要注意的时,消息类型必须在 .proto 文件中定义。下面给出例子:

[whc@linux Contact2.0]$ protoc --decode=contact2.Contact contact.proto < contact.bin
  • contact2.Contact:指定将字符串解码为 contact2.Contact 消息(contact2是package名)
  • contact.proto:指定包含 contact2.Contact 消息的 .proto 文件为contact.proto
  • < contact.bin:因此–decode是从标准输入中读取二进制消息,因此需要将contact.bin文件中的内容重定向到 protoc 中

image-20230606174337029

image-20230606175117227

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

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

相关文章

win10的另一种美化字体的方式,使用noMeiryoUI

目录 1. 前提2. 字体选择3. 查看已经安装的字体并查看效果4. 安装软件修改系统字体5. 修改浏览器字体 1. 前提 21年的时候写了一篇文章&#xff0c;《Windows10下美化字体&#xff0c;达到类似mac的效果》&#xff0c;当时还很迷恋macType这个软件的使用&#xff0c;觉得好牛逼…

Linux——安装VMwaretools实现文件共享功能

作为一个Linux初学者&#xff0c;应该熟悉相关的文件下载和安装以及简单的配置命令&#xff0c;下面通过安装一个VMwaretools来实现window环境和Linux环境下文件的共享功能的实现。 一、安装VMwaretools 点击重新安装后&#xff0c;会在桌面上看到有一个光盘&#xff0c;则代表…

单片机的基础知识和原理

单片机是一种集成了中央处理器&#xff08;CPU&#xff09;、内存、输入/输出&#xff08;I/O&#xff09;端口和其他外设的小型计算机系统。它通常用于嵌入式系统&#xff0c;如家电、汽车电子、工业控制等领域。 下面是单片机的基础知识和原理的概述&#xff1a; 架构&…

Linux系统安装——VMware15.5安装centos7.6 (附资料下载+联网配置+踩坑总结)

一、安装虚拟机VMware15.5 VMware虚拟机安装Win10&#xff0c;安装包秘钥/系统镜像/激活工具如下&#xff1a;(主要这个地址不限速&#xff0c;方便&#xff01;) https://www.123pan.com/s/g520Vv-Dg2id (遇到会拦截下载任务的浏览器&#xff0c;直接换浏览器下载即可&#xf…

【LVS负载均衡群集】

目录 一、企业群集应用概述1、群集的含义2、企业群集分类3、负载均衡群集架构1、负载均衡的结构2、负载均衡群集工作模式分析 二、LVS的三种工作模式1、NAT 模式地址转换 2、TUN模式IP隧道 3、DR模式直接路由 三、实战NAT模式 LVS负载均衡群集部署1.部署共享存储&#xff08;NF…

将安全性置于首位:考试系统源码的安全漏洞和防护措施

在当今数字化时代&#xff0c;考试系统源码的安全性已经成为了一个至关重要的问题。随着更多人开始远程学习和在线考试&#xff0c;考试系统的安全漏洞也越来越容易被利用。因此&#xff0c;在开发和使用考试系统源码应用程序时&#xff0c;保护用户数据和系统资源免受攻击是至…

思维导图整理,100个好看实用的模板分享

思维导图是一款有效提升工作和生活效率的生产力工具&#xff0c;受到全球千万用户的喜爱。 它帮助我们将所需的信息以图形的形式呈现&#xff0c;更加便于我们的记忆。头脑风暴帮助我们捕捉灵感&#xff0c;激发 我们更多的想象力。 它可以应用在我们生活以及工作的方方面面。项…

卡尔曼滤波与组合导航原理(七)平方根Kalman滤波:Potter平方根滤波、SVD分解滤波、UD分解滤波、平方根信息滤波SRIKF

文章目录 一、平方根滤波基本形式二、Potter平方根滤波1、方差阵的量测更新2、方差阵的时间更新3、Potter平方根滤波流程4、向量量测情况下的方差阵量测更新 三、奇异值&#xff08;SVD&#xff09;分解滤波1、时间更新方差方程的SVD分解2、量测更新方差方程的SVD分解3、SVD分解…

西电网课雨课堂《书法鉴赏》全部课后答案

声明&#xff1a;本文CSDN作者原创投稿文章&#xff0c;未经许可禁止任何形式的转载&#xff0c;原文链接 如果图片挂了&#xff0c;可以移步至我的博客西电网课雨课堂《书法鉴赏》答案 - 小木槌 文章目录 绪论--解惑初学书法者&#xff08;上&#xff09;绪论--解惑初学书法者…

Es elasticsearch 十八 Logstash 数据抽取工具

目录 基础 启动命令 简单配置文件 控制台输入输出打印 input从log文件获取数据-如分布式日志收集 监听端口 filter过滤器 Grok 正则捕获 Output 输出到es 文件输入 es输出 获取日志中字段 level 基础 从原数据 获取数据 发送到 某地方 Input{} filter{} output{} …

JMeter 常用的几种断言方法,你会几种呢?

在使用Jmeter进行性能测试或者接口自动化测试工作中&#xff0c;经常会用到的一个功能&#xff0c;就是断言。断言是在请求的返回层面增加一层判断机制。因为请求成功了&#xff0c;并不代表结果一定正确&#xff0c;因此需要判断机制提高测试准确性。本文 主要介绍3种常用的断…

【LSTM】读取时间序列数据 | 时间序列数据的小批量划分方法

由于序列数据本质上是连续的&#xff0c;因此我们在处理数据时需要解决这个问题。当序列过长而不能被模型一次性全部处理时&#xff0c;我们希望能拆分这样的序列以便模型方便读取。 Q&#xff1a;怎样随机生成一个具有n个时间步的mini batch的特征和标签&#xff1f; A&…

4.2 Spark SQL数据源 - 基本操作

一、默认数据源 案例演示读取Parquet文件 查看Spark的样例数据文件users.parquet 1、在Spark Shell中演示 启动Spark Shell 查看数据帧内容 查看数据帧模式 对数据帧指定列进行查询&#xff0c;查询结果依然是数据帧&#xff0c;然后通过write成员的save()方法写入HDF…

还在为开发难度发愁?这个低代码平台能让你轻松搞定!

随着数字技术的不断发展&#xff0c;数字化也越来越深入人们的日常生活中&#xff0c;在这样的时代背景下&#xff0c;企业是需要通过不断创新来满足用户日益增长的业务需求。 使用背景 对企业而言&#xff0c;随着组织规模的扩大&#xff0c;各种业务流程系统需要持续而快速地…

浮点数的存储方式和取值范围

一、有符号整型的编码方式 1. 真值 把用""、"-"表示符号&#xff0c;数值部分用绝对值的编码的表示方式称为真值。 如3用4bit真值表示为0011&#xff0c;-1用真值表示为-0011。 2. 原码 符号位为0表示正数&#xff0c;符号位为1表示负数&#xff0c;数…

网络安全的究竟如何自学?

以十五派9年信息安全教育经验以及数千名学生摸索实践可以得知&#xff0c;在知乎上能够搜索到这个问题的人不外乎三种人&#xff1a; 第一种人在找网络安全入门的捷径&#xff1b; 第二种人想自己的方向进而获得认同感&#xff1b; 第三种人是已经在自学网络安全但是过程中碰…

卡尔曼滤波与组合导航原理(九)Sage-Husa自适应滤波

自适应滤波有很多种方式&#xff0c;也很实用 一、自适应滤波基本思想 函数模型 { X k Φ k / k − 1 X k − 1 Γ k − 1 W k − 1 Z k H k X k V k \left\{\begin{array}{l} \boldsymbol{X}_{k}\boldsymbol{\Phi}_{k / k-1} \boldsymbol{X}_{k-1}\boldsymbol{\Gamma}_{…

Mujoco xml编写(二)

目录 .1 运行 1.1 测试 .2 XML 2,1 基础 2.3 简单实例 2.3 进阶例子 .1 运行 1.1 测试 基于Mujoco210 Ubuntu 22.04配置安装_啥也不是的py人的博客-CSDN博客完成安装后 import mujoco_py import os mj_path mujoco_py.utils.discover_mujoco() xml_path os.path.join…

学习并深入理解闭包

前言 学习闭包前&#xff0c;先学点别的。 程序执行时&#xff1a; 1.编译阶段 创建变量对象GO&#xff0c;包括变量和函数作用域装在一块内存中。但是没有赋值&#xff0c;变量都是undefined,函数&#xff1a;0xxx 2.创建执行上下文 里面有VO对应ao(函数里的变量&#xff0…

数据结构 -- 跳跃链表

跳跃链表的概念 跳跃链表是有序链表的一个变种&#xff0c;在一个有序链表中&#xff0c;查找一个链表中的元素需要进行一次遍历&#xff0c;时间复杂度为O(n)&#xff0c;为了加快查找的过程&#xff0c;能够跳过某些元素呢&#xff1f;一个思路就是牺牲一定的空间换时间&…