文字格式語言規範

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" 應解譯為 靜態 NaN

int32sint32sfixed32範圍從 -0x800000000x7FFFFFFF 的任何 integer 元素。
int64sint64sfixed64範圍從 -0x80000000000000000x7FFFFFFFFFFFFFFF 的任何 integer 元素。
uint32fixed32任何在 00xFFFFFFFF 範圍內的 unsignedInteger 元素。請注意,帶符號的值 (-0) 是無效的。
uint64, fixed64任何在 00xFFFFFFFFFFFFFFFF 範圍內的 unsignedInteger 元素。請注意,帶符號的值 (-0) 是無效的。
string包含有效 UTF-8 資料的 String 元素。任何逸出序列在取消逸出後必須形成有效的 UTF-8 位元組序列。
bytes一個 String 元素,可能包含無效的 UTF-8 逸出序列。
bool一個 Identifier 元素或任何符合以下值的 unsignedInteger 元素。
真值: "True""true""t"1
假值: "False""false""f"0
允許使用 01 的任何無符號整數表示法:000x0010x1 等。
enum 值一個包含枚舉值名稱的 Identifier 元素,或任何在 -0x800000000x7FFFFFFF 範圍內包含枚舉值數字的 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_valuegoogle.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 訊息,其中包含 keyvalue 欄位。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 }
]

keyvalue 欄位都是可選的,如果未指定,則預設為各自型別的零值。如果鍵重複,則在剖析的 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 檔案範例。

# 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 可告知開發人員工具 schema,以便它們可以提供各種功能。

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

以程式方式處理格式

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