protoBuff入门教程

本文介绍了如何在C++中使用ProtocolBuffer进行序列化和反序列化操作。首先,创建了一个.proto文件定义数据结构,然后通过protoc编译器生成C++类。接着,展示了如何编写程序读取用户输入,生成序列化文件,并提供了读取和打印序列化文件内容的程序。文章还强调了在使用ProtocolBuffer时进行版本检查和资源清理的重要性。

注:本文使用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

因为参考文章编译不过,自己有所修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值