Protobuf使用

Protobuf使用

github地址

目錄

  • proto3的更新
  • 定義協議格式
  • 編譯protobuf
  • protobuf_API
    • 枚舉和嵌套類
    • 標準消息方法
    • 解析和序列化
  • 寫一條消息
  • 閱讀消息
  • 編譯
  • Protobuf擴展
  • 優化
  • 高級用法

proto3的更新

  • 在第一行非空白非注釋行,必須寫:
syntax  = "proto3";
  • 字段規則移除了 required,并把 optional 改名為 singular;
    proto2required 也是不推薦使用的。proto3 直接從語法層面上移除了 required規則。其實可以做的更徹底,把所有字段規則描述都撤銷,原來的repeated 改為在類型或字段名后加一對中括號。這樣是不是更簡潔?

  • repeated字段默認采用 packed 編碼;
    proto2 中,需要明確使用 [packed=true] 來為字段指定比較緊湊的 packed 編碼方式。

  • 移除了default 選項;
    proto2 中,可以使用 default 選項為某一字段指定默認值。在 proto3 中,字段的默認值只能根據字段類型由系統決定。也就是說,默認值全部是約定好的,而不再提供指定默認值的語法。
    在字段被設置為默認值的時候,該字段不會被序列化。這樣可以節省空間,提高效率。
    但這樣就無法區分某字段是根本沒賦值,還是賦值了默認值。這在 proto3 中問題不大,但在 proto2 中會有問題。
    比如,在更新協議的時候使用 default 選項為某個字段指定了一個與原來不同的默認值,舊代碼獲取到的該字段的值會與新代碼不一樣。

  • 枚舉類型的第一個字段必須為 0 ;

  • 移除了對分組的支持;
    分組的功能完全可以用消息嵌套的方式來實現,并且更清晰。在 proto2 中已經把分組語法標注為『過期』了。這次也算清理垃圾了。

  • 移除了對擴展的支持,新增了 Any 類型;
    Any 類型是用來替代 proto2 中的擴展的。目前還在開發中。
    proto2 中的擴展特性很像 Swift 語言中的擴展。理解起來有點困難,使用起來更是會帶來不少混亂。
    相比之下,proto3 中新增的 Any 類型有點像 C/C++ 中的 void* ,好理解,使用起來邏輯也更清晰。

  • 增加了 JSON 映射特性;
    語言的活力來自于與時俱進。當前,JSON 的流行有其充分的理由。很多『現代化』的語言都內置了對 JSON 的支持,比如 GoPHP 等。而 C++ 這種看似包羅萬象的學院派語言,因循守舊、故步自封,以致于現出了式微的苗頭。

  • map支持

map<key_type, value_type> map_field = N;
example:
map<string, Project> projects = 3;
  • 在 proto3 中,純數字類型的 repeated 字段編碼時候默認采用 packed 編碼(具體原因見 Protocol Buffer 編碼原理 這一章節)

定義協議格式

.proto文件中的定義很簡單:為要序列化的每個數據結構添加消息,然后為消息中的每個字段指定名稱和類型。這是.proto定義您的消息的文件addressbook.proto

(好的.proto文件命名風格是:packagename.messagename.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 phones = 4;
}message AddressBook {repeated Person people = 1;
}

.proto文件以包聲明開頭,這有助于防止不同項目之間的命名沖突。在C++中,生成的類將放在與包名匹配的命名空間中。

每個元素上的“= 1”,“= 2”標記標識該字段在二進制編碼中使用的唯一“標記”。標簽號1-15需要少于一個字節來編碼而不是更高的數字,因此作為優化,您可以決定將這些標簽用于常用或重復的元素,將標簽16和更高版本留給不太常用的可選元素。重復字段中的每個元素都需要重新編碼標記號,因此重復字段特別適合此優化。

可以指定的最小字段編號為1,最大字段編號為229-1 或 536,870,911。也不能使用數字 19000 到 19999(FieldDescriptor :: kFirstReservedNumber 到 FieldDescriptor :: kLastReservedNumber),因為它們是為 Protocol Buffers實現保留的。

必須使用以下修飾符之一注釋每個字段:

  • required(proto3中移除):必須提供該字段的值,否則該消息將被視為“未初始化”。如果libprotobuf在調試模式下編譯,則序列化未初始化的消息將導致斷言失敗。在優化的構建中,將跳過檢查并始終寫入消息。但是,解析未初始化的消息將始終失敗(通過false從解析方法返回)。除此之外,必填字段的行為與可選字段完全相同。

  • optional(proto3中為singular):該字段可能已設置,也可能未設置。如果未設置可選字段值,則使用默認值。對于簡單類型,您可以指定自己的默認值,就像我們type在示例中為電話號碼所做的那樣。否則,使用系統默認值:數字類型為0,字符串為空字符串,boolsfalse。對于嵌入式消息,默認值始終是消息的“默認實例”或“原型”,其中沒有設置其字段。調用訪問器以獲取尚未顯式設置的可選(或必需)字段的值始終返回該字段的默認值。

  • repeated(proto3默認采用 packed 編碼):該字段可以重復任意次數(包括零)。重復值的順序將保留在協議緩沖區中。將重復字段視為動態大小的數組。

  • proto3 中移除了default選項:字段的默認值只能根據字段類型由系統決定。也就是說,默認值全部是約定好的,而不再提供指定默認值的語法。在字段被設置為默認值的時候,該字段不會被序列化。這樣可以節省空間,提高效率。

編譯protobuf

現在運行編譯器,指定源目錄(應用程序的源代碼所在的位置 - 如果不提??供值,則使用當前目錄),目標目錄(您希望生成的代碼在哪里;通常相同$SRC_DIR) ,以及你的道路.proto。:

protoc -I = $ SRC_DIR --cpp_out = $ DST_DIR $ SRC_DIR / addressbook.proto

這里都生成到當前目錄,輸入

protoc -I=. --cpp_out=. ./addressbook.proto
protoc --cpp_out=. addressbook.proto // 這種也可以

因為您需要C++類,所以使用該--cpp_out選項 - 為其他支持的語言提供了類似的選項。

這將在指定的目標目錄中生成以下文件:

  • addressbook.pb.h,標頭聲明您生成的類。
  • addressbook.pb.cc,其中包含您的類的實現。

protobuf_API

addressbook.pb.h中,可以看到指定的每條消息都有一個類addressbook.proto。對于Person類,可以看到編譯器已為每個字段生成了訪問器。 例如,對于名稱,ID,電子郵件和電話字段,有以下方法:

// name
inline bool has_name() const;
inline void clear_name();
inline const ::std::string& name() const;
inline void set_name(const ::std::string& value);
inline void set_name(const char* value);
inline ::std::string* mutable_name();// id
inline bool has_id() const;
inline void clear_id();
inline int32_t id() const;
inline void set_id(int32_t value);// email
inline bool has_email() const;
inline void clear_email();
inline const ::std::string& email() const;
inline void set_email(const ::std::string& value);
inline void set_email(const char* value);
inline ::std::string* mutable_email();// phones
inline int phones_size() const;
inline void clear_phones();
inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phones() const;
inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phones();
inline const ::tutorial::Person_PhoneNumber& phones(int index) const;
inline ::tutorial::Person_PhoneNumber* mutable_phones(int index);
inline ::tutorial::Person_PhoneNumber* add_phones();

對于字符串 : 一個mutable_讓你獲得指向字符串的直接指針的getter,以及一個額外的setter。請注意,mutable_email()即使email尚未設置,您也可以進行呼叫; 它將自動初始化為空字符串。如果你在這個例子中有一個單數的消息字段,它也有一個mutable_方法但不是一個set_方法。

重復的字段也有一些特殊的方法 - 如果你看一下repeated phones字段的方法,你會發現你可以

  • 檢查重復的字段的_size (換句話說,有多少電話號碼與此相關聯的 Person).

  • 使用索引獲取指定的電話號碼.

  • 更新指定索引處的現有電話號碼.

  • 在郵件中添加另一個電話號碼然后可以編輯(重復的標量類型add_只允許您傳入新值).

有關協議編譯器為任何特定字段定義生成的確切成員的詳細信息,請參閱C ++生成的代碼參考。

枚舉和嵌套類

生成的代碼包含PhoneType與您的.proto枚舉對應的枚舉。您可以參考這個類型Person::PhoneType及其作為值的Person::MOBILEPerson::HOMEPerson::WORK(實現細節是稍微復雜一點,但你并不需要了解他們使用ENUM)。

編譯器還為您生成了一個嵌套類Person::PhoneNumber。如果查看代碼,可以看到實際調用了“真實”類Person_PhoneNumber,但是在內部定義的typedef Person允許您將其視為嵌套類。唯一不同的情況是,如果你想在另一個文件中轉發聲明類 - 你不能在C++中轉發聲明嵌套類型,但你可以轉發聲明Person_PhoneNumber

標準消息方法

每個消息類還包含許多其他方法,可用于檢查或操作整個消息,包括:

  • bool IsInitialized() const; 檢查是否已設置所有必填字段。

  • string DebugString() const; 返回消息的人類可讀表示,對調試特別有用。

  • void CopyFrom(const Person& from); 使用給定消息的值覆蓋消息。

  • void Clear(); 清除所有元素回到空狀態。

以下部分中描述的這些和I / O方法實現了Message所有C ++協議緩沖區類共享的接口。有關詳細信息,請參閱完整的API文檔Message。

解析和序列化

最后,每個協議緩沖區類都有使用協議緩沖區二進制格式編寫和讀取所選類型消息的方法。這些包括:

  • bool SerializeToString(string output) const;* 序列化消息并將字節存儲在給定的字符串中。請注意,字節是二進制的,而不是文本; 我們只將該 string 類用作方便的容器。

  • bool ParseFromString(const string& data); 解析給定字符串中的消息。

  • bool SerializeToOstream(ostream output) const;* 將消息寫入給定的 C++ ostream

  • bool ParseFromIstream(istream input);* 解析來自給定 C++ 的消息 istream

這些只是解析和序列化提供的幾個選項。再次,請參閱MessageAPI參考以獲取完整列表。

寫一條消息

現在嘗試使用協議緩沖類。地址簿應用程序能夠做的第一件事是將個人詳細信息寫入地址簿文件。為此,需要創建并填充協議緩沖區類的實例,然后將它們寫入輸出流。

這是一個程序,它從文件中讀取一個AddressBook,根據用戶輸入在AddressBook文件中添加一個新的Person,然后再將新文本寫回文件。直接調用或引用協議編譯器生成的代碼的部分將突出顯示。

#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;// 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;}}
}// 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;
}

注意GOOGLE_PROTOBUF_VERIFY_VERSION宏。在使用C ++協議緩沖區庫之前執行此宏是一種很好的做法 - 盡管不是絕對必要的。它驗證您沒有意外鏈接到與您編譯的標頭版本不兼容的庫版本。如果檢測到版本不匹配,程序將中止。請注意,每個.pb.cc文件在啟動時都會自動調用此宏。

還要注意ShutdownProtobufLibrary()程序結束時的調用。所有這一切都是刪除協議緩沖區庫分配的所有全局對象。對于大多數程序來說這是不必要的,因為該過程無論如何都要退出,操作系統將負責回收其所有內存。但是,如果您使用需要釋放每個最后一個對象的內存泄漏檢查程序,或者您正在編寫可以由單個進程多次加載和卸載的庫,那么您可能希望強制協議緩沖區清除所有內容。

閱讀消息

當然,如果無法從中獲取任何信息,那么地址簿就不會有多大用處!此示例讀取上面示例創建的文件并打印其中的所有信息。

#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;// 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;}cout << phone_number.number() << 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++ -Wall -std=c++11 write.cpp addressbook.pb.cc -o write `pkg-config --cflags --libs protobuf`
g++ -Wall -std=c++11 read.cpp addressbook.pb.cc -o read `pkg-config --cflags --libs protobuf`

Protobuf擴展

在釋放使用協議緩沖區的代碼之后遲早,您無疑會想要“改進”協議緩沖區的定義。如果你希望你的新緩沖區向后兼容,并且你的舊緩沖區是向前兼容的 - 而且你幾乎肯定想要這個 - 那么你需要遵循一些規則。在新版本的協議緩沖區中:

  • 不得更改任何現有字段的標記號。

  • 不得添加或刪除任何必填字段。

  • 可以刪除可選或重復的字段。

  • 可以添加新的可選或重復字段,但必須使用新的標記號(即從未在此協議緩沖區中使用的標記號,甚至不包括已刪除的字段)。

(這些規則有一些例外,但它們很少使用。)

如果您遵循這些規則,舊代碼將很樂意閱讀新消息并簡單地忽略任何新字段。對于舊代碼,已刪除的可選字段將只具有其默認值,刪除的重復字段將為空。新代碼也將透明地讀取舊消息。但是,請記住舊的消息中不會出現新的可選字段,因此您需要明確檢查它們是否已設置has_,或者在.proto文件中提供合理的默認值[default = value]標簽號后面。如果未為可選元素指定默認值,則使用特定于類型的默認值:對于字符串,默認值為空字符串。對于布爾值,默認值為false。對于數字類型,默認值為零。另請注意,如果添加了新的重復字段,則新代碼將無法判斷它是否為空(通過新代碼)或從未設置(通過舊代碼),因為沒有has_標記。

優化

C ++協議緩沖區庫經過了極大的優化。但是,正確使用可以進一步提高性能。以下是從庫中擠出最后一滴速度的一些提示:

盡可能重用消息對象。消息嘗試保留它們分配用于重用的任何內存,即使它們被清除。因此,如果您連續處理具有相同類型和類似結構的許多消息,則每次重新使用相同的消息對象來加載內存分配器是個好主意。但是,隨著時間的推移,對象會變得臃腫,特別是如果您的消息在“形狀”上有所不同,或者您偶爾構造的消息比平常大得多。

您應該通過調用SpaceUsed方法來監視消息對象的大小,并在它們變得太大時刪除它們。
您的系統內存分配器可能沒有針對從多個線程分配大量小對象進行良好優化。請嘗試使用Google的tcmalloc。

高級用法

協議緩沖區的用途不僅僅是簡單的訪問器和序列化。請務必瀏覽C ++ API參考,以了解您可以使用它們做些什么。

協議消息類提供的一個關鍵特性是反射。您可以迭代消息的字段并操縱它們的值,而無需針對任何特定的消息類型編寫代碼。使用反射的一種非常有用的方法是將協議消息轉換為與其他編碼(例如XMLJSON)之間的轉換。更高級的反射使用可能是找到兩個相同類型的消息之間的差異,或者開發一種“協議消息的正則表達式”,您可以在其中編寫與某些消息內容匹配的表達式。如果您運用自己的想象力,可以將協議緩沖區應用于比您最初預期更廣泛的問題!

Message::Reflection界面 提供反射。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/448093.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/448093.shtml
英文地址,請注明出處:http://en.pswp.cn/news/448093.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大佬(概率期望DP)

首先根據數據范圍&#xff0c;可以判斷基本上是n^2的復雜度 通過分析我們發現每一次都可以從m個數中任意選&#xff0c;既然任意選&#xff0c;那么此時的概率的分母就是不變的&#xff0c;然而題中涉及的是某一段的最大值&#xff0c;所以我們按套路假設 f[i][j]表示第i天&…

vue 父組件 調用 子組件的方法

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 我們都知道通過$ref可以獲取到某個DOM&#xff0c;但是它也可以用來獲取子組件的實例&#xff0c;調用子組件的方法 例&#xff1a; 子…

新手開車13招技巧

開車是一個靠經驗積累技術的過程&#xff0c;新手們往往會在開車時遇到很多問題&#xff0c;我們用本篇文章和新手講述開車的各種技巧&#xff0c;希望每個新手都能從中受益。第1招技巧&#xff1a;上車前要先看車  上車前繞車轉一圈&#xff0c;看車的外況、輪胎、車底下有沒…

高效的數據壓縮編碼方式 Protobuf

高效的數據壓縮編碼方式 Protobuf github地址 目錄 ProtocolBuffers 是什么為什么要發明 ProtocolBuffersproto3 定義 Message 分配字段編號保留字段默認字段規則各個語言標量類型對應關系枚舉枚舉中的保留值允許嵌套枚舉不兼容性更新 Message未知字段Map 類型JsonMapping p…

解決 VUE前端項目報錯:RangeError: Maximum call stack size exceeded

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 我點擊菜單按鈕報錯&#xff1a; RangeError: Maximum call stack size exceeded 2. 原因&#xff1a;參數傳遞有問題或者方法調用有…

新手必須掌握的學車技巧-上坡起步

我們知道&#xff0c;做什么事情都是萬事開頭難&#xff0c;新手們在學車方面更能體會到這一點&#xff0c;正確掌握學車技巧對于新手來說是非常重要的事情&#xff0c;今天&#xff0c;平安學車網&#xff08;www.paxcw.com&#xff09;就會大家探討一下我們學車時必須掌握的是…

高效的序列化/反序列化數據方式 Protobuf

高效的序列化/反序列化數據方式 Protobuf github地址 目錄 protocolBuffers 序列化 Int32StringMapslice序列化小結 protocolBuffers 反序列化 Int32StringMapslice序列化小結 序列化/反序列化性能最后 protocolBuffers序列化 上篇文章中其實已經講過了 encode 的過程&…

如何配置一個Oracle服務

1、網絡服務名&#xff1a;即填寫OracleTNS的值&#xff0c;如OracleTNSorcl_192.168.1.125&#xff0c;填寫orcl_192.168.1.1252、主機名&#xff1a;192.168.1.1253、服務名&#xff1a;orcl4、測試成功即可。 轉載于:https://www.cnblogs.com/dengshiwei/p/4258719.html

解決 VUE前端項目報錯: Uncaught ReferenceError : initPage is not defined (initPage 方法是有的,依舊報錯找不到)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 明明代碼中定義了 initPage 這個方法&#xff0c;&#xff0c;卻一直報找不到這個方法&#xff1a; Uncaught ReferenceError: init…

掌握新手學車技巧對于新手來說是非常重要的

剛開始學車的時候對于新手來說很多操作不知道從哪里下手&#xff0c;這個時候&#xff0c;如果按照相關的學車技巧來學習的話&#xff0c;對于新手來說是非常有好處的。下面我們就來學習一下讓新手們可以快速進入開車狀態的學車技巧吧&#xff01;基本上駕校的教練都會教學員把…

iView學習筆記(三):表格搜索,過濾及隱藏列操作

iView學習筆記(三)&#xff1a;表格搜索&#xff0c;過濾及隱藏某列操作 1.后端準備工作 環境說明 python版本&#xff1a;3.6.6 Django版本&#xff1a;1.11.8 數據庫&#xff1a;MariaDB 5.5.60 新建Django項目&#xff0c;在項目中新建app&#xff0c;配置好數據庫 api_test…

Jenkins自動編譯庫并上傳服務器

Jenkins自動編譯庫并上傳服務器 github地址 首先添加 git 地址&#xff1a; 再添加定時構建&#xff0c;每天夜里構建一次&#xff1a; 執行 shell 腳本進行構建 cd networklayerecho "build json x86" cmake -S . -B cmake-build-release -DCMAKE_BUILD_TYPERele…

解決:The “data“ option should be a function that returns a per-instance value in component definitions

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 只是想定義一個變量&#xff0c;方便頁面上調用 。 報錯&#xff1a; The "data" option should be a function that re…

科目三考試里面的會車,調頭,靠邊停車通過標準

科目三會車&#xff1a;減速靠道路的右側邊緣線行駛&#xff0c;速度要減到20km/h以下&#xff0c;靠右以不壓右側邊緣線為基準盡量靠右。會車結束指令發出后向左打方向回到道路中央。考點&#xff1a;1.速度要降到20km/h&#xff0c;有時考官故意刁難&#xff0c;會在直線行駛…

Esxi直通板載Sata

Esxi安裝好后&#xff0c;打開SSH。 解決方法如下&#xff1a; shell下執行&#xff1a; lspci -v | grep "Class 0106"-B 1&#xff0c;查看是否有如下顯示&#xff1a;0000:00:1f.2 SATAcontroller Mass storage controller: Intel Corporation Lynx Point AHCICon…

gdb 調試 TuMediaService

gdb 調試 TuMediaService github地址 起因 首先需要有 armgdb 環境運行 ./armgdb ./TuMediaService 進入 gdb 模式b hi3531_trcod_interface.cc:98 打斷點r 運行程序print this->vdec_config_path_ 打印關鍵值 在這里我們關注的值已經被修改&#xff0c;由于程序中沒有刻…

jackson 的注解:@JsonProperty、@JsonIgnore、@JsonFormat 用法說明

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 導包&#xff1a; <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-data…

科目三-變更車道,直線行駛和超車的考試標準

直線行駛&#xff1a;這是唯一一個可以提前操作的項目&#xff0c;當聽到“下一項考試為直線行駛......”的指令時&#xff0c;可以立即把車身擺正。放在道路的正中間&#xff0c;并踩油門&#xff0c;把速度提至30----50km/h&#xff0c;最好保持在35---40km/h&#xff0c;因為…

PyQt安裝和環境配置

PyQt安裝和環境配置 github地址 首先安裝Pycharm 新建一個空的 python 工程&#xff0c;找到 setting 安裝第三方模塊 PyQT5 , 點加號&#xff0c;先安 PyQT5 , 再安裝 pyqt5-tools &#xff0c;后面包含 qtdesinger 以上模塊都安完&#xff0c;設置擴展工具的參數找到 sett…

HZOJ 大佬(kat)

及其水水水的假期望&#xff08;然而我已經被期望嚇怕了……&#xff09;。 數據范圍及其沙雕導致丟掉5分…… 因為其實每天的期望是一樣的&#xff0c;考慮分開。 f[i][j]表示做k道題&#xff0c;難度最大為j的概率。 則f[i][j](f[i-1][j])*(j-1)*temq[j]*tem;q為前綴和&#…