テキストフォーマット言語仕様

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

パースの概要

この仕様における言語要素は、字句カテゴリと構文カテゴリに分けられます。字句要素は記述された通りに入力テキストに完全に一致する必要がありますが、構文要素はオプションの WHITESPACE および COMMENT トークンによって区切られる場合があります。

例えば、符号付き浮動小数点値は、符号 (-) と FLOAT リテラルの2つの構文要素から構成されます。符号と数値の間にはオプションの空白やコメントが存在する場合がありますが、数値内には存在しません。例

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.

特に注意が必要なエッジケースが1つあります。数値トークン (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'.

字句要素

以下に記述する字句要素は、大文字の主要要素と小文字の断片という2つのカテゴリに分類されます。構文解析中に使用されるトークンの出力ストリームには主要要素のみが含まれ、断片は主要要素の構成を簡素化するためにのみ存在します。

入力テキストをパースする際、最長一致の主要要素が優先されます。例

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" ) ;

10進整数は、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 *)

8進エスケープシーケンスは、最大3桁の8進数字を消費します。追加の数字はエスケープされずにそのまま通過します。例えば、入力 \1234 をアンエスケープする場合、パーサーは3桁の8進数字 (123) を消費してバイト値 0x53 (ASCII ‘S’, 10進数で83) をアンエスケープし、続く '4' はバイト値 0x34 (ASCII ‘4’) としてそのまま通過します。正しいパースを確実にするため、8進エスケープシーケンスは、必要に応じて先行ゼロを使用し、3桁の8進数字で表現してください。例えば、\000, \001, \063, \377 のようにします。\5Hello のように、数値文字の後に非数値文字が続く場合、3桁未満の数字が消費されます。

16進エスケープシーケンスは、最大2桁の16進数字を消費します。例えば、\x213 をアンエスケープする場合、パーサーは最初の2桁 (21) のみを消費してバイト値 0x21 (ASCII ‘!’) をアンエスケープします。正しいパースを確実にするため、16進エスケープシーケンスは、必要に応じて先行ゼロを使用し、2桁の16進数字で表現してください。例えば、\x00, \x01, \xFF のようにします。\xFHello\x3world のように、数値文字の後に非16進文字が続く場合、2桁未満の数字が消費されます。

バイト単位のエスケープは、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 Table A-1 Extended BNF に従って解釈され、UTF-8 としてエンコードされます。

構文要素

メッセージ

メッセージはフィールドの集合です。テキストフォーマットファイルは単一のメッセージです。

Message = { Field } ;

リテラル

フィールドのリテラル値には、数値、文字列、または true や enum 値のような識別子を指定できます。

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

不明なフィールド

テキストフォーマットパーサーは、フィールド名の代わりに生のフィールド番号として表現された不明なフィールドをサポートできません。これは、6つのワイヤータイプのうち3つがテキストフォーマットで同じ方法で表現されるためです。一部のテキストフォーマットシリアライザの実装では、フィールド番号と値の数値表現を使用するフォーマットで不明なフィールドをエンコードしますが、ワイヤータイプの情報が無視されるため、これは本質的に情報が失われます。比較として、ワイヤーフォーマットは各フィールドタグにワイヤータイプを (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 とマークされたフィールドは、省略するか、1回だけ指定できます。required とマークされたフィールドは、正確に1回だけ指定する必要があります。

関連する .proto メッセージで指定されていないフィールドは、フィールド名がメッセージの reserved フィールドリストに存在しない限り許可されません。reserved フィールドは、どのような形式 (スカラ、リスト、メッセージ) で存在しても、テキストフォーマットによって単に無視されます。

値の型

フィールドに関連付けられた .proto 値の型が既知の場合、以下の値の説明と制約が適用されます。このセクションの目的のため、以下のコンテナ要素を宣言します

signedInteger   = DecSignedInteger | OctSignedInteger | HexSignedInteger ;
unsignedInteger = DecUnsignedInteger | OctUnsignedInteger | HexUnsignedInteger ;
integer         = signedInteger | unsignedInteger ;
.proto 型
float, doubleFloatDecSignedIntegerDecUnsignedInteger のいずれかの要素、または IDENT 部分が "inf""infinity""nan" (大文字小文字を区別しない) のいずれかに等しい Identifier または SignedIdentifier 要素。オーバーフローは無限大または負の無限大として扱われます。8進数および16進数の値は無効です。

注記: "nan"Quiet 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無効な UTF-8 エスケープシーケンスを含む可能性がある String 要素。
bool以下の値のいずれかに一致する Identifier 要素、または unsignedInteger 要素のいずれか。
真値: "True", "true", "t", 1
偽値: "False", "false", "f", 0
0 または 1 の任意の符号なし整数表現が許可されます: 00, 0x0, 01, 0x1 など。
enum 値enum 値名を含む Identifier 要素、または -0x80000000 から 0x7FFFFFFF の範囲で enum 値番号を含む integer 要素のいずれか。フィールドの enum 型定義のメンバーではない名前を指定することは無効です。特定の protobuf ランタイム実装によっては、フィールドの enum 型定義のメンバーではない数値を指定することが有効である場合とそうでない場合があります。特定のランタイム実装に依存しないテキストフォーマットプロセッサ (IDE サポートなど) は、提供された数値が有効なメンバーではない場合に警告を発することを選択できます。"true""infinity" など、他のコンテキストで有効なキーワードである特定の名前も、有効な enum 値名であることに注意してください。
メッセージ値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 フィールド

テキストフォーマットは、マップフィールドのエントリを指定するためのカスタム構文を提供しません。.proto ファイルで map フィールドが定義されている場合、keyvalue フィールドを含む暗黙的な Entry メッセージが定義されます。マップフィールドは常に繰り返し可能であり、複数のキー/値エントリを受け入れます。例

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 フィールドはどちらもオプションであり、指定されていない場合はそれぞれの型のゼロ値がデフォルトとなります。キーが重複している場合、パースされたマップには最後に指定された値のみが保持されます。

テキストプロトではマップの順序は維持されません。

oneof フィールド

テキストフォーマットには oneof フィールドに関連する特別な構文はありませんが、一度に指定できる oneof メンバーは1つだけです。複数のメンバーを同時に指定することは無効です。例

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 でエンコードされます。以下にテキストプロトファイルの例を示します。

# 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 を使用して出力をフォーマットする必要があります。