赞
踩
Protocol Buffer是一个跨语言、跨平台、可扩展的用于序列化和结构化数据的工具,常用于用于通信协议,数据存储等。值得注意的是,protobuf是以二进制来存储数据的。相对于JSON和XML具有以下优点:
体积小
:消息大小只需要XML的1/10 ~ 1/3;速度快
:解析速度比XML快20 ~ 100倍;Protobuf 有两个大的版本:proto2 和 proto3,类似于python 的 2.x 和 3.x 版本,如果是新接触的话,同样建议直接入手 proto3 版本。proto3 相对 proto2 而言,支持更多的语言(Ruby、C#等)、删除了一些复杂的语法和特性、引入了更多的约定等。
protobuf 是用来对数据进行序列化和反序列化。
- 序列化 (Serialization):将数据结构或对象转换成二进制串的过程。
- 反序列化(Deserialization):将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
参考资料如下:
sudo apt-get install autoconf automake libtool curl make g++ unzip
git clone https://github.com/protocolbuffers/protobuf.git
cd protobuf
git submodule update --init --recursive
./autogen.sh
./configure
make
make check
sudo make install
sudo ldconfig
protoc -h
下面只展示了C++的数据类型。其它语言的类型大同小异,详情可以查阅官方。
proto文件消息类型 | C++ 类型 | 说明 |
---|---|---|
double | double 双精度浮点型 | |
float | float | 单精度浮点型 |
int32 | int32 | 使用可变长编码方式,负数时不够高效,应该使用sint32 |
int64 | int64 | 同上 |
uint32 | uint32 | 使用可变长编码方式 |
uint64 | uint64 | 同上 |
sint32 | int32 | 使用可变长编码方式,有符号的整型值,负数编码时比通常的int32高效 |
sint64 | sint64 | 同上 |
fixed32 | uint32 | 总是4个字节,如果数值总是比2^28大的话,这个类型会比uint32高效 |
fixed64 | uint64 | 总是8个字节,如果数值总是比2^56大的话,这个类型会比uint64高效 |
sfixed32 | int32 | 总是4个字节 |
sfixed64 | int64 | 总是8个字节 |
bool | bool | |
string | string | 一个字符串必须是utf-8编码或者7-bit的ascii编码的文本 |
bytes | string | 可能包含任意顺序的字节数据 |
我们先来看一个简单例子:simple.proto
syntax = "proto3";
package tutorial;
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
第一行指定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 次,类似于动态数组。
在当前的目录下执行:
protoc -I=. -I/usr/local/include --cpp_out=. simple.proto
可以将这个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:类的实现
以string password = 3;为例:
- clear_password:清空设置
- password:获得该字段
- set_password:设置该字段
- mutable_password:返回指向该字段的一个指针
- has_password:是否set过
bool IsInitialized() const; //检查是否全部的required字段都被置(set)了值。
void CopyFrom(const Person& from); //用外部消息的值,覆写调用者消息内部的值。
void Clear(); //将所有项复位到空状态(empty state)。
int ByteSize() const; //消息字节大小
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.
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解析消息。
//指定版本 使用protobuf3
syntax = "proto3";
message Account {
//账号
uint64 ID = 1;
//名字
string name = 2;
//密码
string password = 3;
}
编译出pb.cc和pb.h文件
protoc -I=. -I/usr/local/include --cpp_out=. project.proto
#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; }
g++ demo.cpp project.pb.cc -lprotobuf -o main
./main
1000
name
password
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; }
protoc -I=. -I/usr/local/include --cpp_out=. student.proto
//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(); }
g++ test.cpp student.pb.cc -lprotobuf -o main
./main
输出结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。