[Protobuf]常見數據類型以及使用注意事項
@水墨不寫bug
文章目錄
- 一、基本數據類型
- 1、字段
- 2、字段的修飾規則
- 二、自定義數據類型
- 1、message類型
- 2、enum類型
- 3、Any類型
- 4、oneof類型
- 5、map類型
- 三、小工具
- 1.hexdump
- 2.decode
- 四、注意事項
一、基本數據類型
protobuf 支持多種基礎數據類型,常用的有:
類型 | 說明 | 可選值范圍(部分) |
---|---|---|
double | 64位浮點數 | ±1.7E±308 |
float | 32位浮點數 | ±3.4E±38 |
int32 | 使用變長編碼[1]。負數的編碼效率較低?若字段可能為負值,應使用 sint32 代替 | -2^31 到 2^31-1 |
int64 | 使用變長編碼[1]。負數的編碼效率較低?若字段可能為負值,應使用 sint64 代替 | -2^63 到 2^63-1 |
uint32 | 無符號32位整型 ,使用變長編碼[1] | 0 到 2^32-1 |
uint64 | 無符號64位整型 ,使用變長編碼[1] | 0 到 2^64-1 |
sint32 | 有符號32位整型(高效編碼負數) | -2^31 到 2^31-1 |
sint64 | 有符號64位整型(高效編碼負數) | -2^63 到 2^63-1 |
fixed32 | 定長 4 字節。若值常大于2^28 則會比 uint32 更高效。 | 0 到 2^32-1 |
fixed64 | 定長 8 字節。若值常大于2^56 則會比 uint64 更高效。 | 0 到 2^64-1 |
sfixed32 | 固定32位有符號整型(固定長度編碼) | -2^31 到 2^31-1 |
sfixed64 | 固定64位有符號整型(固定長度編碼) | -2^63 到 2^63-1 |
bool | 布爾值 | true/false |
string | 包含 UTF-8 和 ASCII 編碼的字符串,長度不能超過 2^32 | 任意長度字符串 |
bytes | 可包含任意的字節序列但長度不能超過 2^32 | 任意長度字節數組 |
注:[1] 變長編碼是指:經過protobuf 編碼后,原本4字節或8字節的數可能會被變為其他字節數
1、字段
在定義一個字段之后,會給字段設置一個唯一標記值。同一個消息體內,字段標記值不能重復。
message PersonInfo
{int id = 1;string name = 1;//(字段標志重復)
}
消息體內的消息體可以重新計算標記值:
message PersonInfo
{int id = 1;string name = 2;message Phone{string number = 1;//(重新計算標記值)}
}
其次:
字段名稱命名規范:全小寫字母,多個字母之間用 _ 連接。
字段類型分為:基本數據類型 和 特殊類型(包括枚舉enum、消息類型message等)。
字段唯?編號:用來標識字段,?旦開始使用就不能夠再改變
字段編號的范圍為:
1 ~ 536,870,911 (2^29 - 1) ,其中 19000 ~ 19999 不可用
范圍為 1 ~ 15 的字段編號需要一個字節進行編碼, 16 ~ 2047內的數字需要兩個字節進行編碼。編碼后的字節不僅只包含了編號,還包含了字段類型。所以 1 ~ 15要用來標記出現非常頻繁的字段,要為將來有可能添加的、頻繁出現的字段預留一些出來。
2、字段的修飾規則
singular:消息中可以包含該字段零次或?次(不超過?次)。 proto3 語法中,字段默認使用該規則。
repeated:消息中可以包含該字段任意多次(包括零次),其中重復值的順序會被保留。可以理解為定義了?個數組。
二、自定義數據類型
1、message類型
一個message類型被protoc編譯器編譯之后形成對應class。message可以嵌套message,同一層message,字段編號不能重復。message可作為類型來使用,對應class類型組合。
2、enum類型
定義enum類型的值,直接用名稱+常量表示:
例如:
enum PhoneType //枚舉名稱的名稱建議用駝峰命名
{MP = 0;TEL = 1;
}
常量值從0開始,只能為正值,不能為負。枚舉類型也可定義在message體內部。
在同一層級內,不同的枚舉類型內的同名常量值不能相同:
例如:
enum PhoneType
{MP = 0;TEL = 1;
}
enum PhoneTypeCopy
{MP = 0;
}
否則會報錯重定義。
改為不重名即可:
enum PhoneType
{MP = 0;TEL = 1;
}
enum PhoneTypeCopy
{MP_C = 0;
}
類似的還有兩個沒有限制package的文件,import后也會出現上述類似問題。
解決方法是加上package限制。
3、Any類型
泛型類型,可以存放任意類型的數據。
是google直接寫好的,使用前需要
import "google/protobuf/any.proto";
聲明變量的格式如下:
message PeopleInfo
{google.protobuf.Any data = 0;
}
使用any類型需要常用的接口:
// .google.protobuf.Any data = 4;
bool has_data() const;//是否有數據
void clear_data();//清除數據
const ::PROTOBUF_NAMESPACE_ID::Any& data() const;//get
::PROTOBUF_NAMESPACE_ID::Any* mutable_data();//set
//把任意類型轉化為any類型
bool PackFrom(const ::PROTOBUF_NAMESPACE_ID::Message& message);
//重載類型
bool PackFrom(const ::PROTOBUF_NAMESPACE_ID::Message& message,::PROTOBUF_NAMESPACE_ID::ConstStringParam type_url_prefix);
//把any類型轉化為任意類型
bool UnpackTo(::PROTOBUF_NAMESPACE_ID::Message* message) const;
//判斷Any類型是否是T類型
template<typename T> bool Is() const {return _impl_._any_metadata_.Is<T>();}
4、oneof類型
被oneof聲明的多個字段只能保留最后被設置的一個:
oneof other_contact
{string qq = 5;string wechat = 6;
}
qq和微信字段只會保留最后一次被設置的值。
oneof內部不能使用repeated修飾字段。
常用方法包括clear,set,get。不同的是多了一個has方法,用來表明是否有這個方法。
在對應生成的Cpp代碼中,oneof類型定義的多個類型會被轉化為一個enum類型。內部除了oneof內部的類型之外,還有一個NOT_SET = 0常量值。
5、map類型
類似于C++的map,定義protobuf的map需要注意,key_type只能是float,bytes類型之外的標量類型。val_type可以是任意類型。
map不能被repeated修飾。
設置的key-val鍵值對是無序的。
常用方法:size,clear,get,set。
三、小工具
1.hexdump
hexdump是Linux下的?個二進制文件查看工具,它可以將二進制文件轉換為ASCII、八進制、十進制、十六進制格式進行查看。
-C: 表示每個字節顯示為16進制和相應的ASCII字符
這個工具意義在于把protobuf序列化后的二進制轉化為ASCII,便于查看。
2.decode
ProtoBuf 提供的一個命令選項 --decode ,表表示從標準輸入中讀取給定類型的二進制消息,并將其以文本格式寫入標準輸出。 消息類型必須在 .proto 文件或導入的文件中定義。
protoc --decode=contacts.Contacts contacts.proto < contacts.bin
這條命令用于解碼一個用 Protocol Buffers(protobuf)序列化后的二進制文件 contacts.bin
,并將其內容以可讀的文本格式輸出。下面是這條命令的詳細解釋:
-
protoc
這是 Protocol Buffers 的編譯器命令行工具,用于編譯 .proto 文件和處理 protobuf 數據。 -
–decode=contacts.Contacts
這個參數的意思是用.proto
文件中定義的contacts.Contacts
這個消息類型來解碼輸入的數據。contacts
是 proto 文件中的包名(package)。Contacts
是 proto 文件中定義的消息類型(message)。
-
contacts.proto
這是 protobuf 的協議文件,里面定義了數據結構(消息類型、包名等)。protoc
會用它來知道怎么解析二進制數據。 -
< contacts.bin
這表示將contacts.bin
文件中的二進制數據(用 protobuf 序列化過的)作為標準輸入傳給protoc
。
四、注意事項
-
字段編號(tag)不可輕易變更
字段編號 是二進制格式的唯一標識,不能隨意修改或復用,否則會導致兼容性問題。 -
盡量避免刪除字段
如果需要廢棄字段,可用reserved
關鍵字保留該 tag 和字段名。 -
字段類型不能隨意更改
特別是從一種類型改為不兼容的另一種類型(如 int32 改為 string),會導致解析錯誤。 -
repeated 字段適合表達數組或列表
表示 0 個或多個同類型數據項。例如 repeated int32 scores。 -
嵌套 message 和 enum
可以在 message 內部定義子 message 或 enum,便于結構化復雜數據。 -
避免使用 float/double 存儲精確金額
由于浮點數精度問題,金額等精確數據建議用 int64/uint64 表示最小單位(如分、厘)。 -
bytes 與 string 區別
string 專為 UTF-8 文本,bytes 則可存儲任意二進制數據,如圖片、加密內容等。
完~
未經作者同意禁止轉載