《protobuf》基础语法

消息体定义

  • 文件内定义
message Phone
{
	string number = 1;
}

message PeopleInfo
{
	string name = 1;
	int32 age = 2;
	Phone phone = 3;
}
  • 内嵌定义
message PeopleInfo
{
	string name = 1;
	int32 age = 2;
	message Phone
	{
		string number = 1;
	}
	Phone phone = 3;
}
  • 文件外定义
// 文件 Phone.proto
syntax = "proto3";
package phone;

message Phone
{
	string number = 1;
}
import "Phone.proto";
message PeopleInfo
{
	string name = 1;
	int32 age = 2;
	// 引⼊的⽂件声明了package,使⽤消息时,需要⽤ ‘命名空间.消息类型’ 格式
	phone.Phone phone = 3;
}

字段规则

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

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

续回上篇,继续完善contacts.proto文件的内容

syntax = "proto3";
package contacts;

message PeopleInfo
{
	string name = 1;
	int32 age = 2;
	// 采用内嵌式
	message Phone
	{
		string number = 1;
	}
    // repeated 修饰词 修饰的变量相当于数组
	repeated Phone phone = 3;
}

// 通讯录
message Contacts
{
	repeated PeopleInfo contacts = 1;
}

编译选项

补充上篇:

  • -h : protoc -h 表示帮助
    在这里插入图片描述
    上图是所有内容,下面重点讲解一下 - decode
  • decode
    在这里插入图片描述大概意思是:从标准输入读取给定类型的二进制消息,并以文本格式将其写入标准输出。消息类型必须在PROTO FILES或其导入中定义。

先看用法

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

在这里插入图片描述
执行指令后,结果如下
在这里插入图片描述
这个编译条件可以快速查看序列化后二进制文件的内容,是一个很便捷的操作

查看二进制内容的工具还有hexdump

hexdump:是Linux下的一个二进制文件查看工具,它可以将二进制文件转换为ASCII、八进制、十进制、十六进制格式进行查看。
-C: 表示每个字节显示为16进制和相应的ASCII字符

hexdump -C 二进制文件名
eg:hexdump -C contact.bin

在这里插入图片描述

实战:编写一个通讯录文件

上面完善编写了contacts.proto文件,现在完成写文件write.cpp和读文件read.cpp,write.cpp文件主要用于将通讯录信息通过protobuf的序列化,然后写入contact.bin文件内,read.cpp文件主要用于通过protobuf的反序列化,对contact.bin文件内容的读取。

write.cpp的编写

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

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

    cout << "请输⼊联系⼈年龄: ";
    int age;
    cin >> age;
    cin.ignore(256, '\n');  // 清除缓冲区的内容,大于256或遇到'\n'停止
    people->set_age(age);

    for(int i = 1; ;++i)
    {
        cout <<  "请输⼊联系⼈电话" << i << "(只输⼊回⻋完成电话新增): ";
        string number;
        getline(cin, number);
        if(number.empty()) break;
        people->add_phone()->set_number(number);
    }
    cout << "-------------添加联系⼈成功-------------" << endl;
}

int main()
{
	contacts::Contacts con;
	fstream in("contact.bin", ios::in | ios::binary);
	if(!in)
        cout << "contact.bin not exist, it is being created now" << endl;
    // ParseFromIstream方法的作用: 读取in里的内容并将其反序列化保存起来
    else if(!con.ParseFromIstream(&in))
    {
    	cerr << "Fail to parse" << endl;
        in.close();
        return -1;
    }
    in.close();
    
    // 写入通讯录信息
    // 前文有提到,repeated修饰的变量相当于数组,所以
    // con.add_contacts()就相当新分配了一个contacts变量
    AddPeopleInfo(con.add_contacts());
	
	fstream out("contact.bin", ios::out | ios::trunc | ios::binary);
	// SerializeToOstream方法的作用: 读取out里的内容并将其序列化保存起来
	if(!con.SerializeToStream(&out))
	{
		cerr << "Fail to serialize" << endl;
        out.close();
        return -1;
	}
    cout << "write success" << endl;
	out.close();
	
	return 0;
}

执行效果如下:
在这里插入图片描述
在这里插入图片描述

read.cpp的编写

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

using namespace std;

void PrintfContacts(contact2::Contacts &con)
{
    cout << "------------- 通讯录  -------------" << endl;
    for(int i = 0; i < con.contacts_size(); ++i)
    {
        cout << "========= 联系人 " << i+1 << " =========" << endl;
        const contact2::PeopleInfo people = con.contacts(i);
        cout << "姓名:" << people.name() << endl;
        cout << "年龄:" << people.age() << endl;
        for(int j = 0; j < people.phone_size(); ++j)
        {
            auto phone = people.phone(j);
            cout << "号码" << j+1 << ":" << phone.number() << endl;
        }
    }
    cout << "-------------------------------" << endl;
}

int main()
{
    contact2::Contacts con;
    fstream in("contact.bin", ios::in | ios::binary);
    if(!in)
        cout << "contact.bin not exist, it is being created now" << endl;
    else if(!con.ParseFromIstream(&in))
    {
        in.close();
        return -1;
    }

    PrintfContacts(con);
    in.close();
    return 0;
}

执行效果如下:
在这里插入图片描述

补充:主要函数的使用

  • bool ParseFromIstream(std::istream* input);
    Parse 系列的函数有很多,因为前面用了文件流提取内容,所以这里使用 Parse 系列的Istream()。
    作用:读取 input 里的内容并将其反序列化保存起来

  • add_contacts()
    因为 Contacts 里的 contacts 字段被 repeat 修饰,所以相当于数组,这个函数是专门新增一个 contacts 变量的。(ps:因为有contacts所以才是 add_contacts() ,如果是 其他 那么就会对应生成 add_其他()

  • bool SerializeToOstream(std::ostream* output) const;
    作用: 读取 output 里的内容并将其序列化保存起来

  • contacts_size();
    求变量个数(可以认为数组大小),注意 size 前面的名称是变量名称

  • set_变量名();
    代码里有出现,set_age(age),set_name(name),set_number(number) 没错,这些都是设置值的函数

以上函数均包含在 文件.pb.h 里,必要是时可以跳转进去里面学习函数使用,上面仅讲解了使用到的函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值