当前位置:   article > 正文

Protobuf安装、使用以及实例介绍_simple.proto

simple.proto

0. 前言

Protocol Buffer是一个跨语言、跨平台、可扩展的用于序列化和结构化数据的工具,常用于用于通信协议,数据存储等。值得注意的是,protobuf是以二进制来存储数据的。相对于JSON和XML具有以下优点:

  • 1,简洁
  • 2,体积小:消息大小只需要XML的1/10 ~ 1/3;
  • 3,速度快:解析速度比XML快20 ~ 100倍;
  • 4,json\xml都是基于文本格式,protobuf是二进制格式;
  • 5,更好的兼容性,Protobuf设计的一个原则就是要能够很好的支持向下或向上兼容。

在这里插入图片描述

Protobuf 有两个大的版本:proto2 和 proto3,类似于python 的 2.x 和 3.x 版本,如果是新接触的话,同样建议直接入手 proto3 版本。proto3 相对 proto2 而言,支持更多的语言(Ruby、C#等)、删除了一些复杂的语法和特性、引入了更多的约定等。

protobuf 是用来对数据进行序列化和反序列化。

  • 序列化 (Serialization):将数据结构或对象转换成二进制串的过程。
  • 反序列化(Deserialization):将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。

参考资料如下:

1. 安装

  • 安装依赖项
sudo apt-get install autoconf automake libtool curl make g++ unzip
  • 1
  • 下载并生成配置脚本
git clone https://github.com/protocolbuffers/protobuf.git
cd protobuf
git submodule update --init --recursive
./autogen.sh
  • 1
  • 2
  • 3
  • 4
  • 安装
 ./configure
 make
 make check 
 sudo make install
 sudo ldconfig
  • 1
  • 2
  • 3
  • 4
  • 5
  • 测试
protoc -h 
  • 1

2. ProtoBuf数据类型

下面只展示了C++的数据类型。其它语言的类型大同小异,详情可以查阅官方。

proto文件消息类型C++ 类型说明
doubledouble 双精度浮点型
floatfloat单精度浮点型
int32int32使用可变长编码方式,负数时不够高效,应该使用sint32
int64int64同上
uint32uint32使用可变长编码方式
uint64uint64同上
sint32int32使用可变长编码方式,有符号的整型值,负数编码时比通常的int32高效
sint64sint64同上
fixed32uint32总是4个字节,如果数值总是比2^28大的话,这个类型会比uint32高效
fixed64uint64总是8个字节,如果数值总是比2^56大的话,这个类型会比uint64高效
sfixed32int32总是4个字节
sfixed64int64总是8个字节
boolbool
stringstring一个字符串必须是utf-8编码或者7-bit的ascii编码的文本
bytesstring可能包含任意顺序的字节数据

2. 一个简单例子

2.1 proto文件格式讲解

我们先来看一个简单例子:simple.proto

syntax = "proto3";

package tutorial;

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 第一行指定protobuf的版本,这里是以proto3格式定义。还可以指定为proto2。如果没有指定,默认以proto2格式定义。新版的proto必须指定是proto2还是proto3。

  • .proto 文件以一个 package 声明开始,这个声明是为了防止不同项目之间的命名冲突。类似于C++中的命名空间。

  • 每一个字段都要求编码标识号,且唯一

  • 它定义了一个message类型: SearchRequest, 它包含三个字段(field):query、page_number、result_per_page

  • message会被编译成不同的编程语言的相应对象,比如C++中的class、Go中的struct等。

  • 字段前面可以添加修饰,比如required、optional和repeated等。

  • required:必须提供字段值,否则对应的消息会被认为是“未初始化的”。
  • optional:字段值指定与否都可以。如果没有指定一个 optional 的字段值,它就会使用默认值。
  • repeated:字段会重复 N 次,类似于动态数组。

2.2 proto文件运行

在当前的目录下执行:

protoc -I=. -I/usr/local/include --cpp_out=. simple.proto
  • 1

可以将这个proto编译成C++的代码,因为这里我们使用了C++输出格式。go_out用来生成GO代码,java_out产生Java代码,python_out产生python代码,类似地还有csharp_out、objc_out、ruby_out、php_out等参数。

生成的代码我们指定放在本地文件夹中(–cpp_out=.)。这里用.来表示本地文件夹

会生成simple.pb.cc和simple.pb.h文件,如下图所示:
在这里插入图片描述

  • simple.pb.cc:生成类的头文件
  • simple.pb.cc:类的实现

3. proto函数解释

3.1 生成的函数

以string password = 3;为例:

  • clear_password:清空设置
  • password:获得该字段
  • set_password:设置该字段
  • mutable_password:返回指向该字段的一个指针
  • has_password:是否set过

3.2 标准消息函数(Standard Message Methods)

bool IsInitialized() const; //检查是否全部的required字段都被置(set)了值。

void CopyFrom(const Person& from); //用外部消息的值,覆写调用者消息内部的值。

void Clear();	//将所有项复位到空状态(empty state)。

int ByteSize() const;	//消息字节大小
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.3 关于 Debug 的 API

string DebugString() const;	//将消息内容以可读的方式输出

string ShortDebugString() const; //功能类似于,DebugString(),输出时会有较少的空白

string Utf8DebugString() const; //Like DebugString(), but do not escape UTF-8 byte sequences.

void PrintDebugString() const;	//Convenience function useful in GDB. Prints DebugString() to stdout.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.4序列化与反序列化

bool SerializeToString(string* output) const; //将消息序列化并储存在指定的string中。注意里面的内容是二进制的,而不是文本;我们只是使用string作为一个很方便的容器。

bool ParseFromString(const string& data); //从给定的string解析消息。

bool SerializeToArray(void * data, int size) const	//将消息序列化至数组

bool ParseFromArray(const void * data, int size)	//从数组解析消息

bool SerializeToOstream(ostream* output) const; //将消息写入到给定的C++ ostream中。

bool ParseFromIstream(istream* input); //从给定的C++ istream解析消息。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4. 简单的序列化与反序列化实例

4.1 写project.proto并编译出cc和h文件

//指定版本 使用protobuf3
syntax = "proto3";

message Account {
	//账号
	uint64 ID = 1;
	//名字
	string name = 2;
	//密码
	string password = 3;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

编译出pb.cc和pb.h文件

protoc -I=. -I/usr/local/include --cpp_out=. project.proto
  • 1

4.2 序列化与反序列化使用

  • demo.cpp文件:
#include <iostream>
#include <string>
#include "project.pb.h"

int main()
{
	Account account;
	account.set_id(1000);
	account.set_name("name");
	account.set_password("password");

	//序列化
	std::string s = account.SerializeAsString();
	if(s.size() == 0) {
		std::cout << "error in SerializeAsString" << std::endl;
	}
	Account nAccount;
    //反序列化
	if(nAccount.ParseFromString(s)) {
		std::cout << nAccount.id() << std::endl;
		std::cout << nAccount.name() << std::endl;
		std::cout << nAccount.password() << std::endl;
	} else {
		std::cout << "error in ParseFromString" << std::endl;
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 编译:
g++ demo.cpp project.pb.cc -lprotobuf -o main
  • 1
  • 运行
./main
  • 1
  • 输出:
1000
name
password
  • 1
  • 2
  • 3

5. 稍复杂的使用

5.1 student.proto文件与编译

syntax = "proto2";

package tutorial;
message Student{
        required uint64 id = 1;
        required string name =2;
        optional string email = 3;

        enum PhoneType {
                MOBILE = 0;
                HOME = 1;
        }
        message PhoneNumber {
            required string number = 1;
            optional PhoneType type = 2 [default = HOME];
        }
        repeated PhoneNumber phone = 4;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 编译proto文件:
protoc -I=. -I/usr/local/include --cpp_out=. student.proto
  • 1

5.2 使用

  • test.cpp文件
//test.cpp
#include <iostream>
#include <string>
#include "student.pb.h"
using namespace std;

int main(int argc, char* argv[]){
    GOOGLE_PROTOBUF_VERIFY_VERSION;
    
    tutorial::Student student;
    
    //给消息类Student对象student赋值
    student.set_id(201421031059);
    *student.mutable_name()="dablelv";
    student.set_email("dablelv@tencent.com");
    //增加一个号码对象
    tutorial::Student::PhoneNumber* phone_number = student.add_phone();
    phone_number->set_number("15813354925");
    phone_number->set_type(tutorial::Student::MOBILE);
    
    //再增加一个号码对象
    tutorial::Student::PhoneNumber* phone_number1 = student.add_phone();
    phone_number1->set_number("0564-4762652");
    phone_number1->set_type(tutorial::Student::HOME);

    //对消息对象student序列化到string容器
    string serializedStr;
    student.SerializeToString(&serializedStr);
    cout<<"serialization result:"<<serializedStr<<endl; //序列化后的字符串内容是二进制内容,非可打印字符,预计输出乱码
    cout<<endl<<"debugString:"<<student.DebugString();
    
/*----------------上面是序列化,下面是反序列化-----------------------*/
    //解析序列化后的消息对象,即反序列化
    tutorial::Student deserializedStudent;
    if(!deserializedStudent.ParseFromString(serializedStr)){
      cerr << "Failed to parse student." << endl;
      return -1;
    }
    
    cout<<"-------------上面是序列化,下面是反序列化---------------"<<endl;
    //打印解析后的student消息对象 
    cout<<"deserializedStudent debugString:"<<deserializedStudent.DebugString();
    cout <<endl<<"Student ID: " << deserializedStudent.id() << endl;
    cout <<"Name: " << deserializedStudent.name() << endl;
    if (deserializedStudent.has_email()){
    	cout << "E-mail address: " << deserializedStudent.email() << endl;
    }
    for (int j = 0; j < deserializedStudent.phone_size(); j++){
    	const tutorial::Student::PhoneNumber& phone_number = deserializedStudent.phone(j);

      	switch (phone_number.type()) {
            case tutorial::Student::MOBILE:
          	cout << "Mobile phone #: ";
          	break;
            case tutorial::Student::HOME:
                cout << "Home phone #: ";
          	break;
        }
        cout <<phone_number.number()<<endl;
    }

    google::protobuf::ShutdownProtobufLibrary();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 编译
g++ test.cpp student.pb.cc -lprotobuf -o main
  • 1
  • 执行与输出:
./main
  • 1

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/901004
推荐阅读
相关标签
  

闽ICP备14008679号