文字格式語言規範
此格式與例如 .proto
綱要中的文字格式不同。本文件包含使用 ISO/IEC 14977 EBNF 中指定的語法的參考文件。
注意
這是一個從 C++ 文字格式 實作 反向工程而來的草稿規格,可能會根據進一步的討論和審查而變更。儘管已努力使文字格式在支援的語言中保持一致,但可能仍然存在不相容的情況。範例
convolution_benchmark {
label: "NHWC_128x20x20x56x160"
input {
dimension: [128, 56, 20, 20]
data_type: DATA_HALF
format: TENSOR_NHWC
}
}
剖析概觀
本規範中的語言元素分為詞彙和語法類別。詞彙元素必須完全按照描述匹配輸入文字,但語法元素可以用可選的 WHITESPACE
和 COMMENT
符號分隔。
例如,帶正負號的浮點數值包含兩個語法元素:正負號 (-
) 和 FLOAT
常值。正負號和數字之間可能存在可選的空白和註解,但數字內部則不能存在。範例
value: -2.0 # Valid: no additional whitespace.
value: - 2.0 # Valid: whitespace between '-' and '2.0'.
value: -
# comment
2.0 # Valid: whitespace and comments between '-' and '2.0'.
value: 2 . 0 # Invalid: the floating point period is part of the lexical
# element, so no additional whitespace is allowed.
有一個邊緣情況需要特別注意:數字符號 (FLOAT
、DEC_INT
、OCT_INT
或 HEX_INT
) 後面不得立即跟隨 IDENT
符號。範例
foo: 10 bar: 20 # Valid: whitespace separates '10' and 'bar'
foo: 10,bar: 20 # Valid: ',' separates '10' and 'bar'
foo: 10[com.foo.ext]: 20 # Valid: '10' is followed immediately by '[', which is
# not an identifier.
foo: 10bar: 20 # Invalid: no space between '10' and identifier 'bar'.
詞彙元素
以下描述的詞彙元素分為兩個類別:大寫的主要元素和小寫的片段。只有主要元素才會包含在語法分析期間使用的符號輸出串流中;片段的存在只是為了簡化主要元素的建構。
剖析輸入文字時,最長匹配的主要元素獲勝。範例
value: 10 # '10' is parsed as a DEC_INT token.
value: 10f # '10f' is parsed as a FLOAT token, despite containing '10' which
# would also match DEC_INT. In this case, FLOAT matches a longer
# subsequence of the input.
字元
char = ? Any non-NUL unicode character ? ;
newline = ? ASCII #10 (line feed) ? ;
letter = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M"
| "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
| "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m"
| "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
| "_" ;
oct = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" ;
dec = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
hex = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
| "A" | "B" | "C" | "D" | "E" | "F"
| "a" | "b" | "c" | "d" | "e" | "f" ;
空白和註解
COMMENT = "#", { char - newline }, [ newline ] ;
WHITESPACE = " "
| newline
| ? ASCII #9 (horizontal tab) ?
| ? ASCII #11 (vertical tab) ?
| ? ASCII #12 (form feed) ?
| ? ASCII #13 (carriage return) ? ;
識別符號
IDENT = letter, { letter | dec } ;
數值常值
dec_lit = "0"
| ( dec - "0" ), { dec } ;
float_lit = ".", dec, { dec }, [ exp ]
| dec_lit, ".", { dec }, [ exp ]
| dec_lit, exp ;
exp = ( "E" | "e" ), [ "+" | "-" ], dec, { dec } ;
DEC_INT = dec_lit
OCT_INT = "0", oct, { oct } ;
HEX_INT = "0", ( "X" | "x" ), hex, { hex } ;
FLOAT = float_lit, [ "F" | "f" ]
| dec_lit, ( "F" | "f" ) ;
十進制整數可以使用 F
和 f
後綴轉換為浮點值。範例
foo: 10 # This is an integer value.
foo: 10f # This is a floating-point value.
foo: 1.0f # Also optional for floating-point literals.
字串常值
STRING = single_string | double_string ;
single_string = "'", { escape | char - "'" - newline - "\" }, "'" ;
double_string = '"', { escape | char - '"' - newline - "\" }, '"' ;
escape = "\a" (* ASCII #7 (bell) *)
| "\b" (* ASCII #8 (backspace) *)
| "\f" (* ASCII #12 (form feed) *)
| "\n" (* ASCII #10 (line feed) *)
| "\r" (* ASCII #13 (carriage return) *)
| "\t" (* ASCII #9 (horizontal tab) *)
| "\v" (* ASCII #11 (vertical tab) *)
| "\?" (* ASCII #63 (question mark) *)
| "\\" (* ASCII #92 (backslash) *)
| "\'" (* ASCII #39 (apostrophe) *)
| '\"' (* ASCII #34 (quote) *)
| "\", oct, [ oct, [ oct ] ] (* octal escaped byte value *)
| "\x", hex, [ hex ] (* hexadecimal escaped byte value *)
| "\u", hex, hex, hex, hex (* Unicode code point up to 0xffff *)
| "\U000",
hex, hex, hex, hex, hex (* Unicode code point up to 0xfffff *)
| "\U0010",
hex, hex, hex, hex ; (* Unicode code point between 0x100000 and 0x10ffff *)
八進制跳脫序列最多會取用三個八進制數字。其他數字會直接傳遞而不進行跳脫。例如,在跳脫輸入 \1234
時,剖析器會取用三個八進制數字 (123) 來跳脫位元組值 0x83 (ASCII ‘S’),而後續的 ‘4’ 會以位元組值 0x34 (ASCII ‘4’) 直接傳遞。為了確保正確的剖析,請使用 3 個八進制數字來表示八進制跳脫序列,並在需要時使用前導零,例如:\000
、\001
、\063
、\377
。當非數字字元跟在數字字元後面時,會取用少於三個數字,例如 \5Hello
。
十六進制跳脫序列最多會取用兩個十六進制數字。例如,在跳脫 \x213
時,剖析器只會取用前兩個數字 (21) 來跳脫位元組值 0x21 (ASCII ‘!’)。為了確保正確的剖析,請使用 2 個十六進制數字來表示十六進制跳脫序列,並在需要時使用前導零,例如:\x00
、\x01
、\xFF
。當非十六進制字元跟在數字字元後面時,會取用少於兩個數字,例如 \xFHello
或 \x3world
。
僅對類型為 bytes
的欄位使用按位元組跳脫。雖然可以在類型為 string
的欄位中使用按位元組跳脫,但這些跳脫序列必須形成有效的 UTF-8 序列。使用按位元組跳脫來表示 UTF-8 序列容易出錯。對於 string
類型欄位中的不可列印字元和換行字元,最好使用 unicode 跳脫序列。
較長的字串可以分成連續幾行的數個帶引號的字串。例如
quote:
"When we got into office, the thing that surprised me most was to find "
"that things were just as bad as we'd been saying they were.\n\n"
" -- John F. Kennedy"
Unicode 字碼點會根據 Unicode 13 表 A-1 延伸 BNF 進行解譯,並以 UTF-8 格式編碼。
警告
C++ 實作目前將跳脫的高代理字碼點解譯為 UTF-16 程式碼單元,並預期\uHHHH
低代理字碼點緊隨其後,且不會跨多個帶引號的字串分割。此外,不成對的代理項將直接呈現為無效的 UTF-8。這些都是不符合規範的行為[^surrogates],不應依賴這些行為。語法元素
訊息
訊息是欄位的集合。文字格式檔案是單一的訊息。
Message = { Field } ;
常值
欄位常值可以是數字、字串或識別符號,例如 true
或列舉值。
String = STRING, { STRING } ;
Float = [ "-" ], FLOAT ;
Identifier = IDENT ;
SignedIdentifier = "-", IDENT ; (* For example, "-inf" *)
DecSignedInteger = "-", DEC_INT ;
OctSignedInteger = "-", OCT_INT ;
HexSignedInteger = "-", HEX_INT ;
DecUnsignedInteger = DEC_INT ;
OctUnsignedInteger = OCT_INT ;
HexUnsignedInteger = HEX_INT ;
單一字串值可以包含多個帶引號的部分,並以可選的空白分隔。範例
a_string: "first part" 'second part'
"third part"
no_whitespace: "first""second"'third''fourth'
欄位名稱
屬於包含訊息一部分的欄位會使用簡單的 Identifiers
作為名稱。Extension
和 Any
欄位名稱會以方括號括住且完整限定。Any
欄位名稱會加上限定的網域名稱作為前綴,例如 type.googleapis.com/
。
FieldName = ExtensionName | AnyName | IDENT ;
ExtensionName = "[", TypeName, "]" ;
AnyName = "[", Domain, "/", TypeName, "]" ;
TypeName = IDENT, { ".", IDENT } ;
Domain = IDENT, { ".", IDENT } ;
常規欄位和擴充欄位可以有純量值或訊息值。Any
欄位永遠是訊息。範例
reg_scalar: 10
reg_message { foo: "bar" }
[com.foo.ext.scalar]: 10
[com.foo.ext.message] { foo: "bar" }
any_value {
[type.googleapis.com/com.foo.any] { foo: "bar" }
}
未知欄位
文字格式剖析器無法支援以原始欄位編號而不是欄位名稱表示的未知欄位,因為六種線路類型中有三種在文字格式中以相同的方式表示。某些文字格式序列化器實作會以使用欄位編號和值的數值表示的格式編碼未知欄位,但這是本質上有損的,因為會忽略線路類型資訊。相較之下,線路格式是非有損的,因為它在每個欄位標籤中都包含線路類型,格式為 (field_number << 3) | wire_type
。如需編碼的詳細資訊,請參閱編碼主題。
如果沒有訊息綱要中的欄位類型資訊,則無法將值正確編碼為線路格式的 proto 訊息。
欄位
欄位值可以是常值 (字串、數字或識別符號) 或巢狀訊息。
Field = ScalarField | MessageField ;
MessageField = FieldName, [ ":" ], ( MessageValue | MessageList ) [ ";" | "," ];
ScalarField = FieldName, ":", ( ScalarValue | ScalarList ) [ ";" | "," ];
MessageList = "[", [ MessageValue, { ",", MessageValue } ], "]" ;
ScalarList = "[", [ ScalarValue, { ",", ScalarValue } ], "]" ;
MessageValue = "{", Message, "}" | "<", Message, ">" ;
ScalarValue = String
| Float
| Identifier
| SignedIdentifier
| DecSignedInteger
| OctSignedInteger
| HexSignedInteger
| DecUnsignedInteger
| OctUnsignedInteger
| HexUnsignedInteger ;
欄位名稱和值之間的 :
分隔符號對於純量欄位是必要的,但對於訊息欄位 (包括清單) 是可選的。範例
scalar: 10 # Valid
scalar 10 # Invalid
scalars: [1, 2, 3] # Valid
scalars [1, 2, 3] # Invalid
message: {} # Valid
message {} # Valid
messages: [{}, {}] # Valid
messages [{}, {}] # Valid
訊息欄位的值可以用大括號或角括號括住
message: { foo: "bar" }
message: < foo: "bar" >
標示為 repeated
的欄位可以使用重複欄位、使用特殊的 []
清單語法,或兩者組合來指定多個值。值的順序會被保留。範例
repeated_field: 1
repeated_field: 2
repeated_field: [3, 4, 5]
repeated_field: 6
repeated_field: [7, 8, 9]
非 repeated
欄位不能使用清單語法。例如,[0]
對於 optional
或 required
欄位無效。標示為 optional
的欄位可以省略或指定一次。標示為 required
的欄位必須精確指定一次。
除非欄位名稱存在於訊息的 reserved
欄位清單中,否則不允許使用未在相關聯的 .proto 訊息中指定的欄位。如果 reserved
欄位以任何形式 (純量、清單、訊息) 存在,則文字格式會直接忽略這些欄位。
值類型
當已知欄位的相關聯 .proto 值類型時,以下值描述和限制適用。為了本節的目的,我們宣告以下容器元素
signedInteger = DecSignedInteger | OctSignedInteger | HexSignedInteger ;
unsignedInteger = DecUnsignedInteger | OctUnsignedInteger | HexUnsignedInteger ;
integer = signedInteger | unsignedInteger ;
.proto 類型 | 值 |
---|---|
float 、double | Float 、DecSignedInteger 或 DecUnsignedInteger 元素,或 Identifier 或 SignedIdentifier 元素,其 IDENT 部分等於 "inf"、"infinity" 或 "nan" (不區分大小寫)。溢位會被視為無限或 -無限。八進制和十六進制值無效。注意:"nan" 應解譯為 靜態 NaN |
int32 、sint32 、sfixed32 | 範圍從 -0x80000000 到 0x7FFFFFFF 的任何 integer 元素。 |
int64 、sint64 、sfixed64 | 範圍從 -0x8000000000000000 到 0x7FFFFFFFFFFFFFFF 的任何 integer 元素。 |
uint32 、fixed32 | 任何在 0 到 0xFFFFFFFF 範圍內的 unsignedInteger 元素。請注意,帶符號的值 (-0) 是無效的。 |
uint64 , fixed64 | 任何在 0 到 0xFFFFFFFFFFFFFFFF 範圍內的 unsignedInteger 元素。請注意,帶符號的值 (-0) 是無效的。 |
string | 包含有效 UTF-8 資料的 String 元素。任何逸出序列在取消逸出後必須形成有效的 UTF-8 位元組序列。 |
bytes | 一個 String 元素,可能包含無效的 UTF-8 逸出序列。 |
bool | 一個 Identifier 元素或任何符合以下值的 unsignedInteger 元素。真值: "True"、"true"、"t"、1 假值: "False"、"false"、"f"、0 允許使用 0 或 1 的任何無符號整數表示法:00、0x0、01、0x1 等。 |
enum 值 | 一個包含枚舉值名稱的 Identifier 元素,或任何在 -0x80000000 到 0x7FFFFFFF 範圍內包含枚舉值數字的 integer 元素。指定一個非欄位 enum 型別定義成員的名稱是無效的。根據特定的 protobuf 執行階段實作,指定一個非欄位 enum 型別定義成員的數字可能是有效也可能無效。未與特定執行階段實作綁定的文字格式處理器(例如 IDE 支援)可能會在提供的數值不是有效成員時發出警告。請注意,某些在其他上下文中是有效關鍵字的名稱,例如 "true" 或 "infinity",也是有效的枚舉值名稱。 |
message 值 | 一個 MessageValue 元素。 |
擴充欄位
擴展欄位使用它們的完整名稱來指定。範例
local_field: 10
[com.example.ext_field]: 20
擴展欄位通常在其他 .proto 檔案中定義。文字格式語言不提供指定定義擴展欄位檔案位置的機制;相反地,解析器必須事先知道它們的位置。
Any
欄位
文字格式使用類似擴展欄位的特殊語法,支援 google.protobuf.Any
知名型別的擴展形式。範例
local_field: 10
# An Any value using regular fields.
any_value {
type_url: "type.googleapis.com/com.example.SomeType"
value: "\x0a\x05hello" # serialized bytes of com.example.SomeType
}
# The same value using Any expansion
any_value {
[type.googleapis.com/com.example.SomeType] {
field1: "hello"
}
}
在此範例中,any_value
是 google.protobuf.Any
型別的欄位,它儲存一個序列化的 com.example.SomeType
訊息,其中包含 field1: hello
。
group
欄位
在文字格式中,group
欄位使用正常的 MessageValue
元素作為其值,但使用大寫的群組名稱而不是隱含的小寫欄位名稱來指定。範例
message MessageWithGroup {
optional group MyGroup = 1 {
optional int32 my_value = 1;
}
}
根據上述 .proto 定義,以下文字格式是有效的 MessageWithGroup
MyGroup {
my_value: 1
}
與訊息欄位類似,群組名稱和值之間的 :
分隔符號是可選的。
map
欄位
文字格式不提供指定 map 欄位項目的自訂語法。當在 .proto 檔案中定義 map
欄位時,會定義一個隱含的 Entry
訊息,其中包含 key
和 value
欄位。Map 欄位總是重複的,接受多個鍵/值項目。範例
message MessageWithMap {
map<string, int32> my_map = 1;
}
根據上述 .proto 定義,以下文字格式是有效的 MessageWithMap
my_map { key: "entry1" value: 1 }
my_map { key: "entry2" value: 2 }
# You can also use the list syntax
my_map: [
{ key: "entry3" value: 3 },
{ key: "entry4" value: 4 }
]
key
和 value
欄位都是可選的,如果未指定,則預設為各自型別的零值。如果鍵重複,則在剖析的 map 中只會保留最後指定的值。
map 的順序不會在 textproto 中保留。
oneof
欄位
雖然在文字格式中沒有與 oneof
欄位相關的特殊語法,但一次只能指定一個 oneof
成員。同時指定多個成員是無效的。範例
message OneofExample {
message MessageWithOneof {
optional string not_part_of_oneof = 1;
oneof Example {
string first_oneof_field = 2;
string second_oneof_field = 3;
}
}
repeated MessageWithOneof message = 1;
}
上述 .proto 定義會產生以下文字格式行為
# Valid: only one field from the Example oneof is set.
message {
not_part_of_oneof: "always valid"
first_oneof_field: "valid by itself"
}
# Valid: the other oneof field is set.
message {
not_part_of_oneof: "always valid"
second_oneof_field: "valid by itself"
}
# Invalid: multiple fields from the Example oneof are set.
message {
not_part_of_oneof: "always valid"
first_oneof_field: "not valid"
second_oneof_field: "not valid"
}
文字格式檔案
文字格式檔案使用 .txtpb
副檔名,並包含單一 Message
。文字格式檔案使用 UTF-8 編碼。以下提供文字 proto 檔案範例。
重要
.txtpb
是標準的文字格式檔案副檔名,應優先於其他替代方案。此副檔名因其簡潔性,且與官方的線路格式檔案副檔名 .binpb
一致而受到青睞。舊有的標準副檔名 .textproto
仍被廣泛使用,並受到工具的支援。某些工具也支援舊有的副檔名 .textpb
和 .pbtxt
。除了上述以外的所有其他副檔名都**強烈**不建議使用;特別是,諸如 .protoascii
等副檔名錯誤地暗示文字格式僅限於 ascii,而其他諸如 .pb.txt
等副檔名則不被常用工具識別。# This is an example of Protocol Buffer's text format.
# Unlike .proto files, only shell-style line comments are supported.
name: "John Smith"
pet {
kind: DOG
name: "Fluffy"
tail_wagginess: 0.65f
}
pet <
kind: LIZARD
name: "Lizzy"
legs: 4
>
string_value_with_escape: "valid \n escape"
repeated_values: [ "one", "two", "three" ]
標頭
標頭註解 proto-file
和 proto-message
可告知開發人員工具 schema,以便它們可以提供各種功能。
# proto-file: some/proto/my_file.proto
# proto-message: MyMessage
以程式方式處理格式
由於個別 Protocol Buffer 實作不會輸出一致或標準的文字格式,因此修改 TextProto 檔案或輸出 TextProto 的工具或程式庫必須明確使用 https://github.com/protocolbuffers/txtpbfmt 來格式化其輸出。