protocol buffers 使用方法

protocol buffers 使用方法ios

爲何使用 Protocol Buffers

咱們接下來要使用的例子是一個很是簡單的"地址簿"應用程序,它能從文件中讀取聯繫人詳細信息。地址簿中的每個人都有一個名字、ID、郵件地址和聯繫電話。c++

如何序列化和獲取結構化的數據?這裏有幾種解決方案:git

  • 以二進制形式發送/接收原生的內存數據結構。一般,這是一種脆弱的方法,由於接收/讀取代碼必須基於徹底相同的內存佈局、大小端等環境進行編譯。同時,當文件增長時,原始格式數據會隨着與該格式相關的軟件而迅速擴散,這將致使很難擴展文件格式。
  • 你能夠創造一種 ad-hoc 方法,將數據項編碼爲一個字符串——好比將 4 個整數編碼爲 12:3:-23:67。雖然它須要編寫一次性的編碼和解碼代碼且解碼須要耗費一點運行時成本,但這是一種簡單靈活的方法。這最適合編碼很是簡單的數據。
  • 序列化數據爲 XML。這種方法是很是吸引人的,由於 XML 是一種適合人閱讀的格式,而且有爲許多語言開發的庫。若是你想與其餘程序和項目共享數據,這多是一種不錯的選擇。然而,衆所周知,XML 是空間密集型的,且在編碼和解碼時,它對程序會形成巨大的性能損失。同時,使用 XML DOM 樹被認爲比操做一個類的簡單字段更加複雜。

Protocol buffers 是針對這個問題的一種靈活、高效、自動化的解決方案。使用 Protocol buffers,你須要寫一個 .proto 說明,用於描述你所但願存儲的數據結構。利用 .proto 文件,protocol buffer 編譯器能夠建立一個類,用於實現對高效的二進制格式的 protocol buffer 數據的自動化編碼和解碼。產生的類提供了構造 protocol buffer 的字段的 getters 和 setters,而且做爲一個單元來處理讀寫 protocol buffer 的細節。重要的是,protocol buffer 格式支持格式的擴展,代碼仍然能夠讀取以舊格式編碼的數據。github

安裝:
https://github.com/protocolbuffers/protobuf/blob/master/src/README.mdshell

.proto的文件的源代碼:微信

// [START declaration]
syntax = "proto3";
package tutorial;

import "google/protobuf/timestamp.proto";
// [END declaration]
 
// [START messages]
message Person {
  string name = 1;
  int32 id = 2;  // Unique ID number for this person.
  string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }

  repeated PhoneNumber phones = 4;
  google.protobuf.Timestamp last_updated = 5;
}

// Our address book file is just one of these.
message AddressBook {
  repeated Person people = 1;
}
// [END messages]

這裏,咱們對.proto文件所使用的語法進行簡單講解。數據結構

1)protobuf使用的.proto文件以包聲明開始,包聲明和C++中的namespace對應,在某個包聲明中定義的消息,會出如今對應的namespace命名空間中。import語句用來導入其餘.proto文件中的消息定義,這樣就能夠在多個.proto文件中定義消息,而後關聯使用了。ide

2)而後,你須要定義消息結構。一個消息包括多個帶類型的成員。protobuf有許多標準的簡單數據類型,包括bool, int32, float,double以及string, protobuf自帶的.proto文件中也有一些消息結構定義,例如上面出現的google.protobuf.Timestamp。固然,你也能夠根據這些類型,進一步構造其餘消息,例如上面的Person包含了PhoneNumber消息,AddressBook包含了Person消息。你也能夠在其餘消息中定義消息類型,例如上面出如今PhoneNUmber在Person中進行定義。你還能夠定義enum類型,例如上面的PhoneType,包含MOBILE,HOME和WORK三個可選值。佈局

「=1」, 「=2」是用來在二進制編碼中標識對應字段的tag。tag在1-15範圍內只須要一個byte來編碼,而較大的數字須要兩個byte來編碼,因此對於經常使用的那些字段,可使用1-15範圍內的tag。性能

另外,每個tag可使用以下修飾符修飾:

(1)singular: 表示這個字段能夠有一個,也能夠沒有。若是沒有的話,在編碼的時候,不會佔用空間。

(2)repeated: 表示這個字段會重複0次或者更屢次,這個字段裏的值會按照順序編碼。

  1. 定義完了.proto文件,下一步就是編譯這個proto文件,咱們假設這個proto文件名爲addressbook.proto。爲了編譯這個文件,運行以下的語句:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/address.proto

其中-I指定proto文件所在的位置,$DST_DIR指定生成文件所在的位置,這裏--cpp_out表示生成文件爲C++文件,生成目錄在$DST_DIR,$SRC_DIR/addressbook.proto。

若是你在proto所在文件調用上述命令,能夠簡寫以下:

protoc --cpp_out=. addressbook.proto

調用上述命令,生成的文件爲addressbook.pb.h和addressbook.pb.cc。能夠推測,對於xxx.proto,生成文件應該爲xxx.pb.h和xxx.pb.cc。

作成pb格式的文件:

#include <ctime>
#include <fstream>
#include <google/protobuf/util/time_util.h>
#include <iostream>
#include <string>

#include "addressbook.pb.h"

using namespace std;

using google::protobuf::util::TimeUtil;

// This function fills in a Person message based on user input.
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_phones();
    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;
    }
  }
  *person->mutable_last_updated() = TimeUtil::SecondsToTimestamp(time(NULL));
}

// Main function:  Reads the entire address book from a file,
//   adds one person based on user input, then writes it back out to the same
//   file.
int main(int argc, char* argv[]) {
  // Verify that the version of the library that we linked against is
  // compatible with the version of the headers we compiled against.
  GOOGLE_PROTOBUF_VERIFY_VERSION;

  if (argc != 2) {
    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
    return -1;
  }

  tutorial::AddressBook address_book;

  {
    // Read the existing address book.
    fstream input(argv[1], ios::in | ios::binary);
    if (!input) {
      cout << argv[1] << ": File not found.  Creating a new file." << endl;
    } else if (!address_book.ParseFromIstream(&input)) {
      cerr << "Failed to parse address book." << endl;
      return -1;
    }
  }

  // Add an address.
  PromptForAddress(address_book.add_people());

  {
    // Write the new address book back to disk.
    fstream output(argv[1], ios::out | ios::trunc | ios::binary);
    if (!address_book.SerializeToOstream(&output)) {
      cerr << "Failed to write address book." << endl;
      return -1;
    }
  }

  // Optional:  Delete all global objects allocated by libprotobuf.
  google::protobuf::ShutdownProtobufLibrary();

  return 0;
}

解析pb文件

#include <fstream>
#include <google/protobuf/util/time_util.h>
#include <iostream>
#include <string>

#include "addressbook.pb.h"

using namespace std;

using google::protobuf::util::TimeUtil;

// Iterates though all people in the AddressBook and prints info about them.
void ListPeople(const tutorial::AddressBook& address_book) {
  for (int i = 0; i < address_book.people_size(); i++) {
    const tutorial::Person& person = address_book.people(i);

    cout << "Person ID: " << person.id() << endl;
    cout << "  Name: " << person.name() << endl;
    if (person.email() != "") {
      cout << "  E-mail address: " << person.email() << endl;
    }

    for (int j = 0; j < person.phones_size(); j++) {
      const tutorial::Person::PhoneNumber& phone_number = person.phones(j);

      switch (phone_number.type()) {
        case tutorial::Person::MOBILE:
          cout << "  Mobile phone #: ";
          break;
        case tutorial::Person::HOME:
          cout << "  Home phone #: ";
          break;
        case tutorial::Person::WORK:
          cout << "  Work phone #: ";
          break;
        default:
          cout << "  Unknown phone #: ";
          break;
      }
      cout << phone_number.number() << endl;
    }
    if (person.has_last_updated()) {
      cout << "  Updated: " << TimeUtil::ToString(person.last_updated()) << endl;
    }
  }
}

// Main function:  Reads the entire address book from a file and prints all
//   the information inside.
int main(int argc, char* argv[]) {
  // Verify that the version of the library that we linked against is
  // compatible with the version of the headers we compiled against.
  GOOGLE_PROTOBUF_VERIFY_VERSION;

  if (argc != 2) {
    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
    return -1;
  }

  tutorial::AddressBook address_book;

  {
    // Read the existing address book.
    fstream input(argv[1], ios::in | ios::binary);
    if (!address_book.ParseFromIstream(&input)) {
      cerr << "Failed to parse address book." << endl;
      return -1;
    }
  }

  ListPeople(address_book);

  // Optional:  Delete all global objects allocated by libprotobuf.
  google::protobuf::ShutdownProtobufLibrary();

  return 0;
}

執行命令: 

$ g++ addressbook.pb.cc write.cpp -o write `pkg-config --cflags --libs protobuf`

關於命令pkg-config的用法:https://blog.csdn.net/luotuo44/article/details/24836901

  • --cflags:至關於【-I】(大寫的i)屬性
  • --libs:至關於【-L】 和【-l】(小寫的L)屬性

pkg-config可以省略編譯時的一堆參數

c/c++ 學習互助QQ羣:877684253

本人微信:xiaoshitou5854

相關文章
相關標籤/搜索