最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

利用C++開發(fā)一個protobuf動態(tài)解析工具

 更新時間:2023年01月03日 15:35:46   作者:碼小方  
數(shù)據(jù)庫中存儲的protobuf序列化的內(nèi)容,有時候查問題想直接解析查看內(nèi)容。很多編碼在網(wǎng)上很容易找到編解碼工具,但protobuf沒有找到編解碼工具,可能這樣的需求比較少吧,那就自己用C++實現(xiàn)一個,感興趣的可以了解一下

為什么需要這個工具

數(shù)據(jù)庫中存儲的protobuf序列化的內(nèi)容,有時候查問題想直接解析查看內(nèi)容。很多編碼在網(wǎng)上很容易找到編解碼工具,但protobuf沒有找到編解碼工具,可能這樣的需求比較少吧,那就自己用C++實現(xiàn)一個。

需求描述

我們知道,要解析protobuf,需要有proto定義,所以我們的輸入?yún)?shù)需要包含序列化的數(shù)據(jù)以及proto定義,如果proto中包含多個message,還需要指定解析到哪個message。所以一共是三個輸入?yún)?shù)。

此外,為了方便使用,我們的工具不要求給出完整的proto定義,如果有嵌套的message沒有定義,不應(yīng)影響其他字段解析。

開發(fā)

搜索現(xiàn)成方案

網(wǎng)上搜索了一圈,找到的類似方案大多需要導(dǎo)入完整的proto文件:

int DynamicParseFromPBFile(const std::string& file, const std::string& classname, 
      const std::string& pb_str) {
  // ...
  // 導(dǎo)入proto文件
  ::google::protobuf::compiler::Importer importer(&sourceTree, NULL);
  importer.Import(file);

  // 找到要解析的message
  auto descriptor = importer.pool()->FindMessageTypeByName(classname);
  ::google::protobuf::DynamicMessageFactory factory;
  auto message = factory.GetPrototype(descriptor);

  // 動態(tài)創(chuàng)建message對象
  auto msg = message->New();
  msg->ParseFromString(pb_str);
  // msg即為解析到的結(jié)構(gòu)
}

這樣可以實現(xiàn)動態(tài)解析,但仍不滿足我們的需求——即使proto不完整,也希望能解析。

舉個例子:

message MyMsg {
  optional uint64 id = 1;
  optional OtherMsg other = 2;
}

MyMsg中包含OtherMsg類型,但并沒有給出OtherMsg的定義,所以無法正常解析。

AST在哪里

事實上,在解析proto文件時,肯定需要先將其解析為抽象語法樹(AST),在AST中,我們可以很容易修改proto的定義,例如將other字段刪掉,或者將其類型改為bytes,這樣就可以正常解析了。

那么,proto文件解析成的AST結(jié)構(gòu)在哪里呢?只能從源碼中尋找答案了。

一番查找后,終于看到了FindFileByName方法的這段代碼:

bool SourceTreeDescriptorDatabase::FindFileByName(const std::string& filename,
                                                  FileDescriptorProto* output) {
  // ...
  io::Tokenizer tokenizer(input.get(), &file_error_collector);
  
  Parser parser;
  
  // Parse it.
  output->set_name(filename);
  return parser.Parse(&tokenizer, output) && !file_error_collector.had_errors();
}

從這段代碼中可以看到,F(xiàn)ileDescriptorProto就是我們要找的AST結(jié)構(gòu)。那么這到底是個什么結(jié)構(gòu)呢?

其實,F(xiàn)ileDescriptorProto本身也是一個proto定義的message:

message FileDescriptorProto {
  optional string name = 1;     // file name, relative to root of source tree
  optional string package = 2;  // e.g. "foo", "foo.bar", etc.

  // All top-level definitions in this file.
  repeated DescriptorProto message_type = 4;
  repeated EnumDescriptorProto enum_type = 5;
  repeated ServiceDescriptorProto service = 6;
  repeated FieldDescriptorProto extension = 7;

  // ...
}

從它的字段中可以看到,其代表的是整個proto文件,包括文件中的所有message、enum等定義。

開始寫代碼

第一步

仿照上面的源碼,將輸入的proto定義解析為FileDescriptorProto對象:

// proto輸入
istringstream ss(proto);
istream* is = &ss;
io::IstreamInputStream input(is);

// 解析到FileDescriptorProto AST
io::Tokenizer tokenizer(&input, nullptr);
FileDescriptorProto output;
compiler::Parser parser;
if (!parser.Parse(&tokenizer, &output)) {
  err_msg = "parse proto failed";
  return -1;
}
output.set_name("proto");
output.clear_source_code_info();
printf("MSG: proto parsed output: %s\n", output.DebugString().c_str());

第2步

處理FileDescriptorProto對象,將沒有給定義的字段類型都改成bytes,保證proto可以正常解析:

int ConvertUnknownType2Bytes(FileDescriptorProto& file_descriptor_proto) {
  // 找出所有給出定義的message類型名
  set<string> typename_set;
  for (auto const& msgtype : file_descriptor_proto.message_type()) {
    typename_set.insert(msgtype.name());
    // message內(nèi)嵌套定義的message也要包含在內(nèi)
    for (auto const& subtype : msgtype.nested_type()) {
      typename_set.insert(subtype.name());
    }
  }

  // 遍歷所有field,檢查其類型是否存在定義
  for (auto& msgtype : *file_descriptor_proto.mutable_message_type()) {
    for (auto& field : *msgtype.mutable_field()) {
      auto type_name = field.type_name();
      // 基本類型的type_name是空的
      if (!type_name.empty()) {
        // 如果typename_set中找不到該類型名,則轉(zhuǎn)為bytes類型
        if (typename_set.find(type_name) == typename_set.end()) {
          field.clear_type_name();
          field.set_type(FieldDescriptorProto_Type_TYPE_BYTES);
        }
      }
    }
  }
  return 0;
}

第3步

解析修改后的FileDescriptorProto對象,創(chuàng)建指定message類型對象。

// 解析proto并檢查錯誤
SimpleDescriptorDatabase db;
db.Add(output);
DescriptorPool pool(&db);
auto descriptor = pool.FindMessageTypeByName(msg_type_name);
if (descriptor == nullptr) {
  // proto結(jié)構(gòu)有錯
  err_msg = "parse proto failed. FindMessageTypeByName result is null";
  return -1;
}

DynamicMessageFactory factory;
auto message = factory.GetPrototype(descriptor);
unique_ptr<Message> msg(message->New());

第4步

將序列化的數(shù)據(jù)解析到msg中:

msg->ParseFromString(serilized_pb);
cout << "proto msg: " << msg->ShortDebugString().c_str() << endl;

這樣,我們就成功實現(xiàn)了動態(tài)解析,也成功將不可讀的二進(jìn)制數(shù)據(jù)serilized_pb以可讀的形式打印出來了。

總結(jié)

我們?yōu)榱藢崿F(xiàn)動態(tài)解析不完整的proto,我們首先從源碼中找到了將proto定義轉(zhuǎn)化為AST——也就是FileDescriptorProto——的方法。

接著,我們將AST對象進(jìn)行修改,將不合法的proto改成合法的。

最后,我們再利用修改后的FileDescriptorProto構(gòu)造出需要的message對象,解析序列化的數(shù)據(jù)。

到此這篇關(guān)于利用C++開發(fā)一個protobuf動態(tài)解析工具的文章就介紹到這了,更多相關(guān)C++ protobuf動態(tài)解析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言枚舉與聯(lián)合圖文梳理講解

    C語言枚舉與聯(lián)合圖文梳理講解

    枚舉顧名思義就是把所有的可能性列舉出來,像一個星期分為七天我們就可以使用枚舉,聯(lián)合體是由關(guān)鍵字union和標(biāo)簽定義的,和枚舉是一樣的定義方式,不一樣的是,一個聯(lián)合體只有一塊內(nèi)存空間,什么意思呢,就相當(dāng)于只開辟最大的變量的內(nèi)存,其他的變量都在那個變量占據(jù)空間
    2023-01-01
  • C++利用easyx圖形庫實現(xiàn)創(chuàng)意天天酷跑小游戲

    C++利用easyx圖形庫實現(xiàn)創(chuàng)意天天酷跑小游戲

    這篇文章主要為大家詳細(xì)介紹了C++如何利用easyx圖形庫實現(xiàn)創(chuàng)意小游戲——天天酷跑,文中的示例代碼講解詳細(xì),快跟隨小編一起了解一下吧
    2023-03-03
  • 淺析string 與char* char[]之間的轉(zhuǎn)換

    淺析string 與char* char[]之間的轉(zhuǎn)換

    與char*不同的是,string不一定以NULL('\0')結(jié)束。string長度可以根據(jù)length()得到,string可以根據(jù)下標(biāo)訪問。所以,不能將string直接賦值給char*
    2013-10-10
  • 一篇文章帶你了解C++特殊類的設(shè)計

    一篇文章帶你了解C++特殊類的設(shè)計

    這篇文章主要為大家詳細(xì)介紹了C++特殊類的設(shè)計,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • C++實現(xiàn)回文串判斷的兩種高效方法

    C++實現(xiàn)回文串判斷的兩種高效方法

    文章介紹了兩種判斷回文串的方法:解法一通過創(chuàng)建新字符串來處理,解法二在原字符串上直接篩選判斷,兩種方法都使用了雙指針法,文中通過代碼示例講解的非常詳細(xì),需要的朋友可以參考下
    2025-03-03
  • Visual Studio 2022中創(chuàng)建的C++項目無法使用萬能頭<bits/stdc++.h>的解決方案

    Visual Studio 2022中創(chuàng)建的C++項目無法使用萬能頭<bits/stdc++.h>的

    如果大家也遇到下面這種問題,可能是沒有include文件夾中沒有bits/stdc++.h,這篇文章主要介紹了Visual Studio 2022中創(chuàng)建的C++項目無法使用萬能頭<bits/stdc++.h>的解決方案,感興趣的朋友跟隨小編一起看看吧
    2024-02-02
  • C語言利用UDP實現(xiàn)群聊聊天室的示例代碼

    C語言利用UDP實現(xiàn)群聊聊天室的示例代碼

    UDP是一個輕量級、不可靠、面向數(shù)據(jù)報的、無連接的傳輸層協(xié)議,多用于可靠性要求不嚴(yán)格,不是非常重要的傳輸,如直播、視頻會議等等。本文將利用UDP實現(xiàn)簡單的群聊聊天室,感興趣的可以了解一下
    2022-08-08
  • C語言 fseek(f,0,SEEK_SET)函數(shù)案例詳解

    C語言 fseek(f,0,SEEK_SET)函數(shù)案例詳解

    這篇文章主要介紹了C語言 fseek(f,0,SEEK_SET)函數(shù)案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • C++類中三大函數(shù)詳解(構(gòu)造、析構(gòu)和拷貝)

    C++類中三大函數(shù)詳解(構(gòu)造、析構(gòu)和拷貝)

    c++三大函數(shù)指的是拷貝構(gòu)造、拷貝賦值、析構(gòu)函數(shù),下面這篇文章主要給大家介紹了關(guān)于C++類中三大函數(shù)(構(gòu)造、析構(gòu)和拷貝)的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03
  • C++ Boost Archive超詳細(xì)講解

    C++ Boost Archive超詳細(xì)講解

    Boost是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱。Boost庫是一個可移植、提供源代碼的C++庫,作為標(biāo)準(zhǔn)庫的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開發(fā)引擎之一,是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱
    2022-12-12

最新評論

德钦县| 安福县| 政和县| 自治县| 昌宁县| 偃师市| 宝丰县| 成武县| 大城县| 焦作市| 崇信县| 中宁县| 宁远县| 宣威市| 东乡族自治县| 齐齐哈尔市| 广南县| 崇左市| 恩平市| 广水市| 武清区| 定安县| 洛宁县| 西乡县| 榕江县| 恩平市| 宜兴市| 勐海县| 涞水县| 界首市| 故城县| 青川县| 交口县| 兰考县| 西藏| 商丘市| 阳谷县| 新密市| 平凉市| 佛教| 罗田县|