Google protocol buffer 常用序列化和反序列化函數


  首先,protocol buffer(protobuf)是一種輕便高效的結構化數據存儲格式,可以用於結構化數據串行化,或者說序列化。相較XML、json更加得輕便,易懂。它很適合做數據存儲或 RPC 數據交換格式。可用於通訊協議、數據存儲等領域的語言無關、平台無關、可擴展的序列化結構數據格式。常與google的gRPC框架配合使用。目前提供了 C++、Java、Python 三種語言的 API。

  protobuf的基礎內容網上的資料較多,了解可以看 DeveloperWorks 、官方中文文檔


 

最常用的數據格式是message,例如一個訂單數據可以用message表示如下(這些信息將被卸載.proto文件中):

message Order
{
    required uint64 uid = 1;
    required float cost = 2;
    optional string tag = 3;
}

它經過 protobuf 編譯成 c++ 代碼,會生成對應的 XXX.pb.h 和 XXX.pb.cc。message 會對應生成一個 class,里面存放對應的 data members,處理這些數據的函數,以及對應的打包和解包函數。

對於儲存的message格式的數據需要知道的是:

1. 每個字段末尾賦值的 tag:該 tag 是用來標記該字段在序列化后的二進制數據中所在的 field,每個字段的 tag 在 message 內部都是獨一無二的。也不能進行改變,否則數據就不能正確的解包。

2. 數據類型前面的修飾詞:

  • required: 必須賦值,不能為空,否則該條 message 會被認為是“uninitialized”。build 一個“uninitialized” message 會拋出一個 RuntimeException 異常,解析一條 “uninitialized” message 會拋出一條 IOException 異常。除此之外,“required” 字段跟 “optional” 字段並無差別。

  • optional: 字段可以賦值,也可以不賦值。假如沒有賦值的話,會被賦上默認值。

  • repeated: 該字段可以重復任意次數,包括 0 次。重復數據的順序將會保存在 protocol buffer 中,將這個字段想象成一個可以自動設置 size 的數組就可以了。

  • 枚舉

message 數據格式在 c++ 中被 protobuf 自動編譯包含一下內容:

//xxx.proto
message Order
{
    required uint64 uid = 1;
    required float cost = 2;
    optional string tag = 3;
}
 
//xxx.pb.h
class Order : public ::google::protobuf::Message {
 public:
  ...
  // accessors -------------------------------------------------------
 
  // required uint64 uid = 1;
  inline bool has_uid() const;
  inline void clear_uid();
  static const int kUidFieldNumber = 1;
  inline ::google::protobuf::uint64 uid() const;
  inline void set_uid(::google::protobuf::uint64 value);
 
  // required float cost = 2;
  inline bool has_cost() const;
  inline void clear_cost();
  static const int kCostFieldNumber = 2;
  inline float cost() const;
  inline void set_cost(float value);
 
  // optional string tag = 3;
  inline bool has_tag() const;
  inline void clear_tag();
  static const int kTagFieldNumber = 3;
  inline const ::std::string& tag() const;
  inline void set_tag(const ::std::string& value);
  inline void set_tag(const char* value);
  inline void set_tag(const char* value, size_t size);
  inline ::std::string* mutable_tag();
  inline ::std::string* release_tag();
  inline void set_allocated_tag(::std::string* tag);
 
  // @@protoc_insertion_point(class_scope:Order)
 private:
  inline void set_has_uid();
  inline void clear_has_uid();
  inline void set_has_cost();
  inline void clear_has_cost();
  inline void set_has_tag();
  inline void clear_has_tag();
 
  ::google::protobuf::uint32 _has_bits_[1];
 
  ::google::protobuf::uint64 uid_;
  ::std::string* tag_;
  float cost_;
};

對於每一個 message 的 data member,protobuf 會自動生成相關的處理函數,對於每一個字段主要的處理函數有:has_uid(), clear_uid(), uid(), set_uid(),它們分別用於判斷該字段是否被設置,清除該字段設置記錄,獲得該字段,設置該字段。對於示例中的 uid 字段,對應函數的實現如下:

//xxx.pb.h
 
// required uint64 uid = 1;
inline bool Order::has_uid() const {
  return (_has_bits_[0] & 0x00000001u) != 0;
}
inline void Order::set_has_uid() {
  _has_bits_[0] |= 0x00000001u;
}
inline void Order::clear_has_uid() {
  _has_bits_[0] &= ~0x00000001u;
}
inline void Order::clear_uid() {
  uid_ = GOOGLE_ULONGLONG(0);
  clear_has_uid();
}
inline ::google::protobuf::uint64 Order::uid() const {
  // @@protoc_insertion_point(field_get:Order.uid)
  return uid_;
}
inline void Order::set_uid(::google::protobuf::uint64 value) {
  set_has_uid();
  uid_ = value;
  // @@protoc_insertion_point(field_set:Order.uid)
}

 


 

首先是protobuf最基礎/核心的內容,就是結構數據的序列化和反序列化函數:

  通過前面的基礎介紹,相當於我們已經定義了自己的protocol buffer協議,在.proto文件中,且經過了protoc編譯器的編譯現在相當於我們已經有了一個協議,開始應用protobuf提供的序列化和反序列化的API。

//c數組的序列化和反序列化
bool ParseFromArray(const void* data, int size); //反序列化
bool SerializeToArray(void* data, int size) const; //序列化

//序列化
void set_people()             
{
    wp.set_name("sealyao");   
    wp.set_id(123456);        
    wp.set_email("sealyaog@gmail.com");
    wp.SerializeToArray(parray,256);  //將wp序列化,結果保存在parray中,是一個char*
}
//反序列化
void get_people()             
{
    rap.ParseFromArray(parray,256);
    cout << "Get People from Array:" << endl;
    cout << "\t Name : " <<rap.name() << endl;
    cout << "\t Id : " << rap.id() << endl;
    cout << "\t email : " << rap.email() << endl;
}

當然除了c語言的序列化/反序列化還提供了其他一些API:

//C++ String的序列化和反序列化API
bool SerializeToString(string* output) const;
bool ParseFromString(const string& data);
//序列化
void set_people()             
{
    wp.set_name("sealyao");   
    wp.set_id(123456);        
    wp.set_email("sealyaog@gmail.com");
    wp.SerializeToString(&pstring);
}
//反序列化
void get_people()             
{
    rsp.ParseFromString(pstring);  
    cout << "Get People from String:" << endl;
    cout << "\t Name : " <<rsp.name() << endl;
    cout << "\t Id : " << rsp.id() << endl;
    cout << "\t email : " << rsp.email() << endl;
}

 

//文件描述符的序列化和序列化API
bool SerializeToFileDescriptor(int file_descriptor) const;
bool ParseFromFileDescriptor(int file_descriptor);
//序列化
void set_people()
{
    fd = open(path,O_CREAT|O_TRUNC|O_RDWR,0644);
    if(fd <= 0){
        perror("open");
        exit(0); 
    }   
    wp.set_name("sealyaog");
    wp.set_id(123456);
    wp.set_email("sealyaog@gmail.com");
    wp.SerializeToFileDescriptor(fd);   
    close(fd);
}
//反序列化
void get_people()
{
    fd = open(path,O_RDONLY);
    if(fd <= 0){
        perror("open");
        exit(0);
    }
    rp.ParseFromFileDescriptor(fd);
    std::cout << "Get People from FD:" << endl;
    std::cout << "\t Name : " <<rp.name() << endl;
    std::cout << "\t Id : " << rp.id() << endl;
    std::cout << "\t email : " << rp.email() << endl;
    close(fd);
}

 

//C++ stream序列化和反序列化API
bool SerializeToOstream(ostream* output) const;
bool ParseFromIstream(istream* input);
 
//序列化
void set_people()
{
    fstream fs(path,ios::out|ios::trunc|ios::binary);
    wp.set_name("sealyaog");
    wp.set_id(123456);
    wp.set_email("sealyaog@gmail.com");
    wp.SerializeToOstream(&fs);    
    fs.close();
    fs.clear();
}
//反序列化
void get_people()
{
    fstream fs(path,ios::in|ios::binary);
    rp.ParseFromIstream(&fs);
    std::cout << "\t Name : " <<rp.name() << endl;
    std::cout << "\t Id : " << rp.id() << endl; 
    std::cout << "\t email : " << rp.email() << endl;   
    fs.close();
    fs.clear();
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM