首页 > 编程语言 >【C++】protobuf的简单使用(通讯录例子)

【C++】protobuf的简单使用(通讯录例子)

时间:2024-08-10 20:53:40浏览次数:16  
标签:PeopleInfo string contacts protobuf proto C++ 通讯录 类型 cout

protobuf的简单使用(通讯录例子)

.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
在这里插入图片描述
在这里插入图片描述


       新人创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看。

标签:PeopleInfo,string,contacts,protobuf,proto,C++,通讯录,类型,cout
From: https://blog.csdn.net/tang20030306/article/details/141093438

相关文章

  • LeetCode 算法:最小栈 c++
    原题链接......
  • c++中内存管理
    一、内存划分1、分区介绍(1)栈栈又称做堆栈,用于存储非静态局部变量、函数参数、返回值等,栈的空间是向下增长的。(2)内存映射段内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(3)堆堆用于程序运行时动态内存分......
  • 【C++面向对象】重载
    重载简述重载是C++面向对象编程领域的重要概念。C++允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。对于重载函数而言,有一个基本原则:重载的函数两两之间的参数列表互不相同。参数列表不同是指参数的数量不同,或者参数的类型不同,但C++并......
  • 扩展【从0制作自己的ros导航小车】C++_ROS_QT5联合编译,简单界面为ROS开发增添交互
    从0制作自己的ros导航小车前言一、环境搭建二、联合编译三、测试前言前面已经实现了导航功能,对于之后的一些开发,有交互能力是比较重要的,比如小车上连接一块屏幕,通过屏幕来选择模式,可视化等等。QT是不错的选择,但是需要做一些额外的工作,让QT与ROS能够建立联系,实现通信......
  • 集合相似度c++
    初入新蒟蒻一多多关照。弱弱问一句,有没有东营区一中的学哥学姐                               集合相似度题目是这样的——题目描述给定两个整数集合,它们的相似度定义为:Nc/Nt×100%。其中Nc是两个集合......
  • C++基础入门
    一·命名空间(namespace)正常namespace的使用include<stdio.h>#include<stdlib.h>//1.正常的命名空间定义//wzh是命名空间的名字,⼀般开发中是⽤项⽬名字做命名空间名。namespacebit{//命名空间中可以定义变量/函数/类型intrand=10;intAdd(in......
  • 1.动手编写第一个makefile编译c++多文件项目
    1.动手编写第一个makefile编译c++多文件项目1.1ubuntu开发环境安装•apt-getupdate#更新安装源•apt-getinstallg++#安装gcc和c++的开发库•apt-getinstallgdb#调试工具•apt-getinstallmake•apt-getinstallopenssh-server#远程连接工具•apt-getin......
  • 2024年华为OD机试真题-推荐多样性-C++-OD统一考试(C卷D卷)
    2024年OD统一考试(D卷)完整题库:华为OD机试2024年最新题库(Python、JAVA、C++合集) 题目描述:推荐多样性需要从多个列表中选择元素,一次性要返回N屏数据(窗口数量),每屏展示K个元素(窗口大小),选择策略:1.各个列表元素需要做穿插处理,即先从第一个列表中为每屏选择一个元素,再从第二个列......
  • c++习题18-计算鞍点
    目录一,题目二,思路三,代码一,题目给定一个5×5的矩阵,每行只有一个最大值,每列只有一个最小值,寻找这个矩阵的鞍点。鞍点指的是矩阵中的一个元素,它是所在行的最大值,并且是所在列的最小值。例如:在下面的例子中(第4行第1列的元素就是鞍点,值为8)。11356912478101056......
  • c++习题20-百钱百鸡
    目录一,题目二,思路三,代码 一,题目农夫约翰今天来到了畜牧市场,想给自己的农场里买点鸡回去。已知公鸡一只5块钱,母鸡3块钱,小鸡崽子一块钱三只。农夫手里有N元,他想买N只鸡,但是他跟ljc一样数学不好,想让你帮他算一算有几种买法,以方便他挑选。输入描述一个整数N,约翰手里的钱......