ProtoBuf—2

news2024/11/18 11:38:27

文章目录

  • 1、字段规则
  • 2、消息类型的定义和使用
  • 3、enum的使用
  • 4、any类型
  • 2、oneof类型
  • 3、map类型

1、字段规则

消息的字段可以用以下几种规则来修饰:

  • singular:消息中可以包含该字段零次或一次(不超过一次)。proto3语法中,字段默认使用该规则
  • repeated:消息中可以包含该字段任意多次(包括0次),其中重复值的顺序会保留。可以理解为定义了一个数组

更新contacts.proto,PeopleInfo 消息中新增 phone 字段,表示一个联系人有多个号码,可将其设置为repeated,写法如下:

//首行:语法指定行
syntax = "proto3";
package contacts2;

message Phone
{
    string number = 1;
}

//定义联系人message
message PeopleInfo 
{
    string name = 1;    //姓名
    int32 age = 2;      //年龄
    repeated Phone phone = 3;
}

message 是可以嵌套的,并且嵌套层数没有限制

//定义联系人message
message PeopleInfo 
{
    string name = 1;    //姓名
    int32 age = 2;      //年龄
    
    message Phone
    {
        string number = 1;
    }
    repeated Phone phone = 3;
}

如果Phone定义在其他proto文件中,则应该为

#phone.proto
// 首行:语法指定行
syntax = "proto3";
package phone;

message Phone
{
    string number = 1;
}

#contacts.proto
//首行:语法指定行
syntax = "proto3";
package contacts2;
import "phone.proto";

//定义联系人message
message PeopleInfo 
{
    string name = 1;    //姓名
    int32 age = 2;      //年龄
    repeated phone.Phone phone = 3;
}

2、消息类型的定义和使用

将通讯录序列化,并写入文件中

#include <iostream>
#include <fstream>
#include "contacts.pb.h"
using namespace std;

void AddPeopleInfo(contacts2::PeopleInfo *people)
{
    cout << "新增联系人" << endl;
    cout << "请输入联系人姓名:";
    string name;
    getline(cin, name);
    people->set_name(name);

    cout << "请输入联系人年龄:";
    int age;
    cin >> age;
    people->set_age(age);
    cin.ignore(256, '\n');

    for (int i = 0;; ++i)
    {
        cout << "请输入联系人电话" << i + 1 << "(只输入回车完成电话新增):";
        string number;
        getline(cin, number);
        if (number.empty())
        {
            break;
        }
        contacts2::PeopleInfo_Phone *phone = people->add_phone();
        phone->set_number(number);
    }

    cout << "添加联系人成功" << endl;
}

int main()
{
    contacts2::Contacts contacts;

    // 读取本地已经存在的通讯录文件
    fstream input("contacts.bin", ios::in | ios::binary);
    if (!input)
    {
        cout << "contacts.bin not find, create new file!" << endl;
    }
    else if (contacts.ParseFromIstream(&input))
    {
        cerr << "parse error" << endl;
        input.close();
        return -1;
    }
    // 向通讯录中添加一个联系人
    AddPeopleInfo(contacts.add_contacts());

    // 将通讯录写入本地文件
    fstream output("contacts.bin", ios::out | ios::trunc | ios::binary);
    if (!contacts.SerializeToOstream(&output))
    {
        cerr << "write error!" << endl;
        input.close();
        output.close();
        return -1;
    }
    return 0;
}

编译:

g++ -o write write.cc contacts.pb.cc -std=c++11 -lprotobuf

运行:
在这里插入图片描述

从文件中将通讯录解析出来,并进行打印

#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.number() << 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;
}

编译:

g++ -o read read.cc contacts.pb.cc -std=c++11 -lprotobuf

运行:
在这里插入图片描述

因为protobuf是以二进制进行序列化,存储到文件中,如果需要查看,可以采用命令:

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

contacts2.Contacts:表示哪个包中的message
contacts.proto:表示对应的文件
contacts.bin:表示对应的二进制文件

结果:
在这里插入图片描述

可以使用命令 protoc -h 查看其他参数选项

3、enum的使用

当需要定义一个消息类型的时候,可能想为一个字段指定某“预定义值序列”中的一个值,就比如通讯录中的电话类型有移动电话和固定电话,这时候可以通过枚举实现。

enum PhoneType
{
    MP = 0;  //proto3版本中,首成员必须为0,成员不应有相同的值   
    TEL = 1;
}

枚举类型不仅可以定义在protobuf文件的最外层,也可以定义在message内部

message phone
{
    enum PhoneType
    {
        MP = 0;  //proto3版本中,首成员必须为0,成员不应有相同的值   
        TEL = 1;
    } 
}

如果在同一个protobuf文件中,不同enum有相同的字段名,在编译时则会报错,例如:

syntax = "proto3";

enum PhoneType
{
    MP = 0;  //proto3版本中,首成员必须为0,成员不应有相同的值   
    TEL = 1;
}

enum PhoneTypeCopy
{
    MP = 0;  //proto3版本中,首成员必须为0,成员不应有相同的值   
    TEL = 1;
}

在这里插入图 片描述

如果两个protobuf文件使用了相同的枚举常量名称,编译时也会报错:

#test_enum.proto
syntax = "proto3";
import "test_enum2.proto";

enum PhoneType
{
    MP = 0;  //proto3版本中,首成员必须为0,成员不应有相同的值   
    TEL = 1;
}

#test_enum2.proto
syntax = "proto3";

enum PhoneTypeCopy
{
    MP = 0;  //proto3版本中,首成员必须为0,成员不应有相同的值   
}

在这里插入图片描述

解决方法:给其中任意一个文件添加package,或者两个文件都添加package

对通讯录进行升级

//首行:语法指定行
syntax = "proto3";
package contacts2;


//定义联系人 message
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;   //电话信息
}

枚举的默认值是编号为0的值

更新对应的通讯录

//write.cc
#include <iostream>
#include <fstream>
#include "contacts.pb.h"
using namespace std;

void AddPeopleInfo(contacts2::PeopleInfo *people)
{
	//......
	//和前面一致
    for (int i = 0;; ++i)
    {
    	//......
    	//和前面一致
        phone->set_number(number);

        cout << "请输入该电话类型(1.移动电话  2.固定电话): ";
        int type;
        cin >> type;
        cin.ignore(256, '\n');
        switch (type)
        {
        case 1:
            phone->set_type(contacts2::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP);
            break;
        case 2:
            phone->set_type(contacts2::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL);
            break;
        default:
            cout << "选择有误" << endl;
            break;
        }
    }

    cout << "添加联系人成功" << endl;
}
//read.cc
void PrintContacts(contacts2::Contacts &contacts)
{
    for (int i = 0; i < contacts.contacts_size(); ++i)
    {
		//......
		//和前面一致
        for (int j = 0; j < people.phone_size(); ++j)
        {
            const ::contacts2::PeopleInfo_Phone &phone = people.phone(j);
            cout << "电话" << j + 1 << ":" << phone.number();
            // 联系人电话1:123456789 (MP)
            cout << "     (" << phone.PhoneType_Name(phone.type()) << ")" << endl;
        }
    }
}

4、any类型

在 Protocol Buffers(Protobuf)中,Any 类型是一种特殊的消息类型,用于存储任意类型的数据。它提供了一种灵活的方式来在消息中嵌入其他类型的消息,而无需提前知道具体的消息结构。
可以将Any类型理解为泛型类型

使用Any类型,需要导入google/protobuf/any.proto
给PeopleInfo添加Address

对contacts.proto进行更新

//首行:语法指定行
syntax = "proto3";
package contacts2;

import "google/protobuf/any.proto";


message Address
{
    string home_address = 1; //家庭住址
    string unit_address = 2; //单位地址
}

//定义联系人 message
message PeopleInfo 
{
	//......
	//和前面一致
    google.protobuf.Any data = 4;
}

更新对应的通讯录

//write.cc
void AddPeopleInfo(contacts2::PeopleInfo *people)
{
	//......
	//和前面一致
    contacts2::Address address;
    cout<<"请输入联系人的家庭地址"<<endl;
    string home_address;
    getline(cin, home_address);
    address.set_home_address(home_address);
    cout<<"请输入联系人的单位地址"<<endl;
    string unit_address;
    getline(cin, unit_address);
    address.set_unit_address(unit_address);
    //将Address对象转换为Any
    if(!people->mutable_data()->PackFrom(address))
    {
        cerr << "转换失败" << endl;
    }

    cout << "添加联系人成功" << endl;
}
//read.cc
void PrintContacts(contacts2::Contacts &contacts)
{
    for (int i = 0; i < contacts.contacts_size(); ++i)
    {
    	//......
    	//和前面一致
        // 判断有没有为data进行设值,并且为Address类型
        if (people.has_data() && people.data().Is<contacts2::Address>())
        {
            contacts2::Address address;
            // 将people中的data转换为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;
            }
        }
    }
}

2、oneof类型

在 Protocol Buffers(Protobuf)中,oneof 是一种特殊的字段声明,它允许在一个消息中定义多个互斥的字段,只能同时选择其中一个进行赋值。

使用 oneof 类型可以有效地在消息结构中表示多个可能的字段,但只允许其中一个字段被设置为非默认值。这在某些情况下可以帮助简化消息的定义和使用,并节省存储空间。

比如在通讯录中,除了电话号码之外,还需要保留一种通信方式,比如微信和qq,但两者只能选一种,此时就能使用 oneof 类型

对contacts.proto进行修改

message PeopleInfo 
{
	//......
	//和前面一致
    oneof other_contact
    {
        //repeated string qq = 5; 不能使用repeated,否则编译时会报错
        string qq = 5;
        string wechat = 6;
    }
    
}

注意:oneof 中的字段不能使用 repeated 进行修饰

更改对应的通讯录

//write.cc
void AddPeopleInfo(contacts2::PeopleInfo *people)
{
	//......
	//和前面一致
    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->set_qq(qq);
    }
    else if (2 == other_contact)
    {
        cout << "请输入联系人微信号:";
        string wechat;
        getline(cin, wechat);
        people->set_wechat(wechat);
    }
    else
    {
        cout << "选择有误,未成功设置其他联系方式!" << endl;
    }

    cout << "添加联系人成功" << endl;
}
//read.cc
void PrintContacts(contacts2::Contacts &contacts)
{
    for (int i = 0; i < contacts.contacts_size(); ++i)
    {
    	//......
    	//和前面一致
        switch (people.other_contact_case())
        {
        case contacts2::PeopleInfo::OtherContactCase::kQq:
            cout << "联系人qq: " << people.qq() << endl;
            break;
        case contacts2::PeopleInfo::OtherContactCase::kWechat:
            cout << "联系人微信: " << people.wechat() << endl;
            break;
        default:
            break;
        }
    }
}

3、map类型

在 Protocol Buffers(Protobuf)中,map用于表示键值对的集合。它提供了一种方便的方式来在消息中存储和传输映射关系的数据。

在 Protobuf 中,Map 类型需要指定键(key)和值(value)的类型。键必须是基本数据类型(例如 int32、string)或枚举类型,而值可以是任意的消息类型。map中的数据是无序的

并且map字段不能用 repeated 修饰

对contacts.proto进行修改,给PeopleInfo添加备注信息

message PeopleInfo 
{
	//......
	//和前面一致
    map<string, string> remark = 7; //备注信息
}

更改对应的通讯录

//write.cc
void AddPeopleInfo(contacts2::PeopleInfo *people)
{
	//......
	//和前面一致
    for(int i = 0;; ++i)
    {
        cout<<"请输入备注"<<i+1<<"标题(只输入回车完成备注新增):";
        string remark_key;
        getline(cin, remark_key);
        if(remark_key.empty())
        {
            break;
        }
        cout<<"请输入备注"<<i+1<<"内容:";
        string remark_value;
        getline(cin, remark_value);
        people->mutable_remark()->insert({remark_key, remark_value});
    }

    cout << "添加联系人成功" << endl;
}
//read.cc
void PrintContacts(contacts2::Contacts &contacts)
{
    for (int i = 0; i < contacts.contacts_size(); ++i)
    {
    	//......
    	//和前面一致
        if (people.remark_size())
        {
            cout << "备注信息:" << endl;
        }
        for (auto it = people.remark().cbegin(); it != people.remark().end(); ++it)
        {
            cout << "  " << it->first << ":" << it->second << endl;
        }
    }
}

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

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

相关文章

【XML技术】web杂谈(3)之深入理解什么是XML、XML的语法详解

涉及知识点 什么是 XML&#xff0c;XML的特征&#xff0c;XML的基本语法及应用&#xff0c;应用程序接口&#xff08;DOM&SAX&#xff09;&#xff0c;XML的文档的显示&#xff0c;深入了解XML技术。 原创于&#xff1a;CSDN博主-《拄杖盲学轻声码》&#xff0c;更多内容可…

马氏距离mahalanobis distance

来看个题 【解】 代码验证 import numpy as np from scipy.spatial.distance import mahalanobis# 定义四个点坐标的数组 points np.array([[0.5, 1.5], [4, 4], [8, 6], [2, 2]])# 计算协方差矩阵 covariance_matrix np.cov(points.T)# 计算马氏距离 mean np.mean(points,…

策略模式Strategy介绍与C++实现

目录 1、策略模式的提出&#xff1a; 2、策略模式定义&#xff1a; 3、策略模式总结&#xff1a; 4、需求描述&#xff1a; 5、普通实现 6、使用策略模式实现 1、策略模式的提出&#xff1a; 在软件构建过程中&#xff0c;某些对象使用的算法可能多种多样&#xff0c;经…

Ubuntu下 NVIDIA Container Runtime 安装与使用

NVIDIA Container Runtime官网 GitHub仓库&#xff1a;Docker 是开发人员采用最广泛的容器技术。借助 NVIDIA Container Runtime&#xff0c;开发人员只需在创建容器期间注册一个新的运行时&#xff0c;即可将 NVIDIA GPU 暴露给容器中的应用程序。用于 Docker 的 NVIDIA Conta…

自然语言处理:任务与应用

文章目录 一、基本任务1.词性标注Part-of-speech (POS) tagging2.命名实体识别Named Entity Recognition (NER)3.共指消解Coreference Resolution4.句法依赖解析Syntactic Dependency Parsing5.中文分词Chinese Word Segmentation 二、常见应用1.文本匹配2. 知识图谱3. 问答系统…

微服务中的负载均衡

如图 有三处需要负载均衡 这里主要讲内部的grpc调用的 负载均衡 图中3的地方 ① 集中式load balance ② 进程内load balance ③独立进程load balance 首选第二种方式

华为MateBook 16 2021款锐龙版R7(CREM-WFD9)(CREM-WFG9)原装出厂Win11系统工厂模式,恢复原厂系统

HUAWEI华为笔记本电脑&#xff0c;MateBook 16 2021款 锐龙版 R7(CREM-WFD9) (CREM-WFG9) 原厂Windows11系统&#xff0c;带F10恢复功能&#xff0c;原装OEM专用系统&#xff0c;恢复出厂状态 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、华为电脑管家等预装程序 …

Java启动项目时,控制台报端口被占用,解决办法

端口占用一般是现在有别的程序正在使用该端口 第一步可以先查出来是哪个程序在占用&#xff1a; netstat -aon | find "8113" //8113为被占用的端口号 此时的结果是这样的&#xff0c;查询出来的为占用此端口程序的PID&#xff08; 25472 &#xff09; 然后根据…

隐式迭代是什么意思?jQuery选择器隐式迭代

在使用jQuery 选择器获取元素后&#xff0c;如果不考虑获取到的元素数量&#xff0c;直接对元素进行操作&#xff0c;则在操作时会发生隐式迭代。隐式迭代是指&#xff0c;当要操作的元素实际有多个时&#xff0c;jQuery 会自动对所有的元素进行操作&#xff0c;示例代码如下。…

Centos 系统中使用 Firefix 播放视频 - VLC播放器的安装

问题&#xff1a; 出于刷视频的需要&#xff0c;需要使用虚拟机&#xff08;Centos7&#xff09;上的 Firefix 来播放视频&#xff0c;经确认安装 flash 的方式是不行的。事实上在 Firefix 播放视频仅需要安装 VLC 播放器就可以了&#xff0c;以下记录安装 VLC 视频播放器的过程…

Selenium教程__截图(16)

selenium截图有两种方式 截取全屏 get_screenshot_as_file(filename)&#xff1a;将截图转化成文件保存到本地&#xff0c;filename为保存的文件路径get_screenshot_as_base64()&#xff1a;将截图转化成base64get_screenshot_as_png()&#xff1a;将截图转化成png 截取指定元…

Java学习记录之struts2(一)

1 struts入门 1.1 介绍  框架&#xff1a;就是半成品&#xff0c;框架已经完成一些通用功能。只需要实现与业务有关功能。  版本&#xff1a;struts-2.3.15.3-all 1.2 核心功能 1.3 目录结构  apps / struts2-blank.war 最简答环境  apps / struts2-showcase.war 常…

【Linux】MySQL 主从复制与读写分离

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 MySQL 主从复制与读写分离 MySQL 主从复制与读写分离1、什么是读写分离2、为什么要读写分离3、什么时候要读写分离4、主从复制与读写分离5、mysql支持的复制类型6、主从复制的…

Unity Android启动子进程加载其他SDK

1、导出untiy 安卓工程 添加MyService 2、 在子进程内加载SDK部分

【C#】错误 14 “GenerateResource”任务意外失败。

问题描述 Microsoft Visual Studio Ultimate 2013 12.0.21005.1 错误 14 “GenerateResource”任务意外失败。 System.Runtime.InteropServices.ExternalException (0x80004005): GDI 中发生一般性错误。在 System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder,…

开车适合佩戴哪种蓝牙耳机,分享几款骨传导耳机的使用体验

骨传导耳机从发布到现在有了几年的时间&#xff0c;也有很多人开始了解&#xff0c;但真正去选购的人还是很少&#xff0c;如果你没有使用过骨传导耳机&#xff0c;在选购时会很迷茫。作为一个骨传导耳机的重度使用者&#xff0c;下面就给大家分享一下我的体验感受以及选购建议…

大模型微调实践遗留问题1

Lora 和 AdaLora的区别和联系&#xff1f; P-tuning和Prompt tuning的区别和联系&#xff1f; P-Tuning&#xff0c;仅对大模型的Embedding加入新的参数。 P-Tuning-V2&#xff0c;将大模型的Embedding和每一层前都加上新的参数。 Prompt Tuning 方式可以看做是 Prefix Tuni…

自学黑客(网络安全),一般人我劝你还是算了吧(自学网络安全学习路线--第十二章 无线网络安全下)【建议收藏】

文章目录 一、自学网络安全学习的误区和陷阱二、学习网络安全的一些前期准备三、自学网络安全学习路线一、无线局域网安全性分析1、Wi-Fi网络安全2、WEP存在如下弱点&#xff1a; 二、移动通信网安全防护1、3G安全体系趋于透明化2、考虑采用公钥密码体系3、考虑新密码技术的应用…

kafka入门,生产者自定义分区(六)

1、实现Partitioner接口 package com.longer.producer;import org.apache.kafka.clients.producer.Partitioner; import org.apache.kafka.common.Cluster;import java.util.Map;/*** 实现接口Partitioner* 实现3个方法&#xff1a;partition&#xff0c;close,configure* 编写…

EasyCVR级联后上级在线,请求播放显示端口不可达是什么原因?

EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&#xff0c;比如&#xff1a;视…