文字格式語言規範

Protocol buffer 文字格式語言規範定義了一種以文字形式表示 protobuf 資料的語法,這通常對於組態或測試很有用。

此格式與 .proto 結構描述中文字的格式不同,例如。本文件包含參考文件,其中使用 ISO/IEC 14977 EBNF 中指定的語法。

範例

convolution_benchmark {
  label: "NHWC_128x20x20x56x160"
  input {
    dimension: [128, 56, 20, 20]
    data_type: DATA_HALF
    format: TENSOR_NHWC
  }
}

剖析總覽

本規範中的語言元素分為詞法和語法類別。詞法元素必須與輸入文字完全符合描述,但語法元素可以使用選用的 WHITESPACECOMMENT 符號分隔。

例如,帶正負號的浮點數值包含兩個語法元素:正負號 (-) 和 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.

有一個需要特別注意的邊緣情況:數字符號 (FLOATDEC_INTOCT_INTHEX_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" ) ;

十進位整數可以使用 Ff 後綴轉換為浮點數值。範例

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。

語法元素

訊息

訊息是欄位的集合。文字格式檔案是單一訊息。

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 作為名稱。ExtensionAny 欄位名稱會以方括號括住並完全限定。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] 對於 optionalrequired 欄位無效。標記為 optional 的欄位可以省略或指定一次。標記為 required 的欄位必須精確指定一次。

除非欄位名稱存在於訊息的 reserved 欄位清單中,否則不允許使用未在相關聯的 .proto 訊息中指定的欄位。reserved 欄位 (如果以任何形式 (純量、清單、訊息) 存在) 會被文字格式簡單地忽略。

值類型

當欄位相關聯的 .proto 值類型已知時,以下值描述和約束適用。為了本節的目的,我們宣告以下容器元素

signedInteger   = DecSignedInteger | OctSignedInteger | HexSignedInteger ;
unsignedInteger = DecUnsignedInteger | OctUnsignedInteger | HexUnsignedInteger ;
integer         = signedInteger | unsignedInteger ;
.proto 類型
floatdoubleFloatDecSignedIntegerDecUnsignedInteger 元素,或 IdentifierSignedIdentifier 元素,其 IDENT 部分等於 "inf""infinity""nan" (不區分大小寫)。溢位被視為無限大或負無限大。八進位和十六進位值無效。

注意:"nan" 應解譯為 Quiet NaN

int32sint32sfixed32範圍 -0x800000000x7FFFFFFF 內的任何 integer 元素。
int64sint64sfixed64範圍 -0x80000000000000000x7FFFFFFFFFFFFFFF 內的任何 integer 元素。
uint32fixed32範圍 00xFFFFFFFF 內的任何 unsignedInteger 元素。請注意,帶正負號的值 (-0) 無效。
uint64fixed64範圍 00xFFFFFFFFFFFFFFFF 內的任何 unsignedInteger 元素。請注意,帶正負號的值 (-0) 無效。
string包含有效 UTF-8 資料的 String 元素。任何跳脫序列在解跳脫時都必須形成有效的 UTF-8 位元組序列。
bytesString 元素,可能包含無效的 UTF-8 跳脫序列。
boolIdentifier 元素或任何符合以下值之一的 unsignedInteger 元素。
True 值: "True""true""t"1
False 值: "False""false""f"0
允許使用 01 的任何不帶正負號的整數表示法:000x0010x1 等。
列舉值包含列舉值名稱的 Identifier 元素,或範圍 -0x800000000x7FFFFFFF 內包含列舉值數字的任何 integer 元素。指定非欄位 enum 類型定義成員的名稱無效。根據特定的 protobuf 執行階段實作,指定非欄位 enum 類型定義成員的數字可能有效,也可能無效。未繫結到特定執行階段實作的文字格式處理器 (例如 IDE 支援) 可以在提供的數字值不是有效成員時選擇發出警告。請注意,某些在其他上下文中是有效關鍵字的名稱 (例如 "true""infinity") 也是有效的列舉值名稱。
訊息值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_valuegoogle.protobuf.Any 類型的欄位,它儲存了包含 field1: hello 的序列化 com.example.SomeType 訊息。

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 訊息,其中包含 keyvalue 欄位。Map 欄位始終是 repeated,接受多個鍵/值項目。範例

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 }
]

keyvalue 欄位都是選用的,如果未指定,則預設為其各自類型的零值。如果索引鍵重複,則在剖析的 map 中只會保留最後指定的值。

map 的順序不會在 textprotos 中保留。

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 檔案範例。

# 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-fileproto-message 會將結構描述告知開發人員工具,以便它們可以提供各種功能。

# proto-file: some/proto/my_file.proto
# proto-message: MyMessage

以程式設計方式使用格式

由於個別 Protocol Buffer 實作發出的文字格式既不一致也不標準,因此修改 TextProto 檔案或發出 TextProto 輸出的工具或程式庫必須明確使用 https://github.com/protocolbuffers/txtpbfmt 來格式化其輸出。