注:本文使用proto3.7版本做测试
proto文件及编译
1、写一个proto文件:
syntax = "proto3";
package tutorial;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phone = 4;
}
message AddressBook {
repeated Person person = 1;
}
repeated: 这个字段可能会重复任意次(包括零)。repeated值的顺序会保存在protocol buffer中。repeated字段将补当作动态数组。
2、shell下编译proto文件指令如下:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto
因为你想用C++的类,所以你要写--cpp_out选项——类似的选项对其他受支持的语言也有提供。
此时会在输出路径下多两个c++文件:
addrBook.pb.cc
addrBook.pb.h
proto常用接口
解析和序列化
bool SerializeToString(string* output) const;:序列化消息并存储给定字符串的字节。 注意这个字节是二进制的,而不是文本;我们只使用string类作为一个方便的容器。bool ParseFromString(const string& data);:从给定字符串解析消息。bool SerializeToOstream(ostream* output) const;:将消息写入给定的c++ ostream上。bool ParseFromIstream(istream* input);:从给定的c++ istream上解析消息。
写一个序列化文件
程序名writeMsg.cpp.实现功能shell里面输入电话薄信息,生成序列化文件test代码如下:
#include <iostream>
#include <fstream>
#include <string>
#include "addrBook.pb.h"
using namespace std;
void PromptForAddress(tutorial::Person* person)
{
cout << "Enter person ID number: ";
int id;
cin >> id;
person->set_id(id);
cin.ignore(256, '\n');
cout << "Enter name: ";
getline(cin, *person->mutable_name());
cout << "Enter email address (blank for none): ";
string email;
getline(cin, email);
if (!email.empty()) {
person->set_email(email);
}
while (true) {
cout << "Enter a phone number (or leave blank to finish): ";
string number;
getline(cin, number);
if (number.empty()) {
break;
}
tutorial::Person_PhoneNumber* phone_number = person->add_phone();
phone_number->set_number(number);
cout << "Is this a mobile, home, or work phone? ";
string type;
getline(cin, type);
if (type == "mobile") {
phone_number->set_type(tutorial::Person::MOBILE);
} else if (type == "home") {
phone_number->set_type(tutorial::Person::HOME);
} else if (type == "work") {
phone_number->set_type(tutorial::Person::WORK);
} else {
cout << "Unknown phone type. Using default." << endl;
}
}
}
int main(int argc,char* argv[])
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
//验证有没有意外地用了一个版本不兼容的库的头文件
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
tutorial::AddressBook addrBook;
{
fstream input(argv[1],ios::in|ios::binary);
if (!input) {
cout << argv[1] << ": File not found. Creating a new file." << endl;
}
else if(!addrBook.ParseFromIstream(&input))
{
cerr << "Failed to parse address book." << endl;
return -1;
}
}
PromptForAddress(addrBook.add_person());
{
fstream output(argv[1], ios::out | ios::trunc | ios::binary);
if (!addrBook.SerializeToOstream(&output)) {
cerr << "Failed to write address book." << endl;
return -1;
}
}
google::protobuf::ShutdownProtobufLibrary();
//删除Protocol Buffer库分配的所有的全局对象。
return 0;
}
注意两点:
1、GOOGLE_PROTOBUF_VERIFY_VERSION宏:这是一种很好的做法——尽管不是严格必需的——使用C++ Protocol Buffer库之前执行这个宏。它验证了你有没有意外地用了一个版本不兼容的库的头文件。如果检测到一个版本不匹配,则程序将终止。注意,每个.pb.cc文件在启动时会自动嵌入这个宏。
2、的ShutdownProtobufLibrary():这都是为了删除Protocol Buffer库分配的所有的全局对象。这对大多数程序是不必要的,因为这个进程是要退出的,操作系统将会负责回收所有的内存。然而,如果你使用一个内存泄漏检查器,会要求每一个对象被释放,或者如果您正在编写一个可能多次被一个进程加载或卸载的库,那么你可能会想强迫Protocol Buffers去清理这一切
编译生成文件writeMsg
g++ writeMsg.cpp addrBook.pb.cc -o writeMsg `pkg-config --cflags --libs protobuf`
解析序列化文件
写一个解析序列化文件程序readMsg.cpp,将解析的数据打印到终端。程序如下:
#include <iostream>
#include <fstream>
#include <string>
#include "addrBook.pb.h"
using namespace std;
void ListPeople(const tutorial::AddressBook& address_book)
{
for (int i = 0; i < address_book.person_size(); i++) {
const tutorial::Person& person = address_book.person(i);
cout << "Person ID: " << person.id() << endl;
cout << " Name: " << person.name() << endl;
cout << " E-mail address: " << person.email() << endl;
for (int j = 0; j < person.rm(); j++) {
const tutorial::Person_PhoneNumber& phone_number = person.phone(j);
switch (phone_number.type()) {
case tutorial::Person_PhoneType_MOBILE:
cout << " Mobile phone #: ";
break;
case tutorial::Person_PhoneType_HOME:
cout << " Home phone #: ";
break;
case tutorial::Person_PhoneType_WORK:
cout << " Work phone #: ";
break;
}
cout << phone_number.number() << endl;
}
}
}
int main(int argc, char* argv[]) {
GOOGLE_PROTOBUF_VERIFY_VERSION;
//验证有没有意外地用了一个版本不兼容的库的头文件
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
tutorial::AddressBook addrBook;
{
fstream input(argv[1], ios::in | ios::binary);
if (!addrBook.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
ListPeople(addrBook);
google::protobuf::ShutdownProtobufLibrary();
//删除Protocol Buffer库分配的所有的全局对象。
return 0;
}
测试结果如下:
./writeMsg test
test: File not found. Creating a new file.
Enter person ID number: 10
Enter name: xiaoming
Enter email address (blank for none): 10101010@qq.com
Enter a phone number (or leave blank to finish): 123456789
Is this a mobile, home, or work phone? mobile
Enter a phone number (or leave blank to finish): 1234567
Is this a mobile, home, or work phone? home
Enter a phone number (or leave blank to finish):
./readMsg test
Person ID: 10
Name: xiaoming
E-mail address: 10101010@qq.com
Mobile phone #: 123456789
Home phone #: 1234567
参考文章:在C++中使用Protocol Buffer_——-CSDN博客_c++ protocolbuf
因为参考文章编译不过,自己有所修改。
本文介绍了如何在C++中使用ProtocolBuffer进行序列化和反序列化操作。首先,创建了一个.proto文件定义数据结构,然后通过protoc编译器生成C++类。接着,展示了如何编写程序读取用户输入,生成序列化文件,并提供了读取和打印序列化文件内容的程序。文章还强调了在使用ProtocolBuffer时进行版本检查和资源清理的重要性。
3万+

被折叠的 条评论
为什么被折叠?



