NBT のテキスト表現。コマンドやデバッグ出力で使われる形式で、 本ライブラリはパースと出力の両方に対応する。
1.21.5 で導入された拡張構文を含む。記述は Java版 26.2 で確認した。
前提: 10 NBT バイナリ形式
value = compound | list | array | string | number | boolean | function ;
compound = "{" [ pair { "," pair } [ "," ] ] "}" ;
pair = key ":" value ;
key = bare_string | quoted_string ;
list = "[" [ value { "," value } [ "," ] ] "]" ;
array = "[" ("B" | "I" | "L") ";" [ value { "," value } [ "," ] ] "]" ;
number = [ sign ] ( int_literal | float_literal ) [ suffix ] ;
int_literal = dec_digits | "0x" hex_digits | "0b" bin_digits ;
float_literal = dec_digits "." [ dec_digits ] [ exponent ]
| "." dec_digits [ exponent ]
| dec_digits exponent ;
exponent = ("e" | "E") [ sign ] dec_digits ;
suffix = [ "u" | "s" ] ( "b" | "B" | "s" | "S" | "l" | "L" | "f" | "F" | "d" | "D" ) ;
boolean = "true" | "false" ;
function = ("bool" | "uuid") "(" value ")" ;
string = bare_string | quoted_string ;
bare_string = ( "a".."z" | "A".."Z" | "0".."9" | "_" | "-" | "." | "+" )+ ;
quoted_string = '"' { char | escape } '"' | "'" { char | escape } "'" ;数値リテラルの区切り文字 _ は桁の間に置ける(123_456 = 123456)。
| 記法 | NBT 型 |
|---|---|
1b 1B |
Byte |
1s 1S |
Short |
1 |
Int |
1l 1L |
Long |
1.0f 1.0F |
Float |
1.0 1.0d 1.0D |
Double |
true false |
Byte(1b / 0b) |
[B; 1b, 2b] |
ByteArray |
[I; 1, 2] |
IntArray |
[L; 1L, 2L] |
LongArray |
[1, 2] |
List<Int> |
{} |
Compound |
| その他 | String |
接尾辞のない整数は Int、小数点や指数を含む数は Double。
Int の範囲を超える接尾辞なし整数は MALFORMED_DATA(暗黙に Long へ格上げしない)。
符号接尾辞 u / s は幅接尾辞の前に置く(10ub = 符号なしとして解釈した後に Byte へ格納)。
u 付きの値が対象幅の符号なし最大値を超えたら MALFORMED_DATA。
0x に続く b B d D f F は16進数字として解釈する。
したがって 16進リテラルに使える幅接尾辞は s S l L だけとする。
0xFF -> Int 255 (FF は 16進数字)
0xFFb -> Int 4091 (b も 16進数字。Byte 接尾辞ではない)
0xFFl -> Long 255
0xFFs -> Short 255
これは Minecraft の実装が明示していない曖昧点で、
どちらに解釈しても文法上は成り立ってしまう。
黙って一方を選ぶと全言語で挙動がずれるため、ここで明示的に固定する。
Byte / Float / Double を16進で書きたい場合は10進表記を使う。
2進リテラル (0b) の数字は 0 と 1 だけなので、この曖昧さは起きない。
全ての幅接尾辞を使える。ただし 0b 単体は「10進の 0 に Byte 接尾辞」と解釈する
(0b は真偽値の false として広く使われているため)。
Infinity / -Infinity / NaN を受理する。幅接尾辞を付けられる。
接尾辞が無ければ Double。
bool(x) は x が 0 以外なら 1b、0 なら 0b を返す。
uuid("...") は UUID を IntArray 4 要素(上位から順)へ変換する。
引用符は " と ' のどちらも使える。文字列内では使っていない方の引用符をそのまま書ける。
| エスケープ | 意味 |
|---|---|
\\ |
\ |
\" \' |
引用符 |
\b \s \t \n \f \r |
制御文字(\s は空白) |
\xNN |
8bit 値 |
\uNNNN |
UTF-16 コード単位 |
\UNNNNNNNN |
コードポイント |
\N{名前} |
Unicode 文字名(例: \N{SNOWMAN}) |
出力側は " で囲み、次のものだけをエスケープする。
| 対象 | 出力 |
|---|---|
" \ |
\" \\ |
\b \t \n \f \r |
同じ短縮形 |
その他の制御文字 (U+0000〜U+001F, U+007F) |
\uXXXX(小文字16進4桁) |
孤立サロゲート (U+D800〜U+DFFF で対になっていないもの) |
\uXXXX |
補助文字(正しいサロゲートペア)はエスケープせず、そのまま出す。 UTF-16 を基本とする言語(C# / Java)では 2 つのコード単位に分かれて見えるため、 素朴に「サロゲートならエスケープ」と実装すると コードポイント単位の言語(Python / Rust)と出力が食い違う。 判定は必ず「対になっているか」で行う。
\N{...} は入力としてのみ受理し、出力では使わない(実装間で名前表が揃わないため)。
1.21.5 以降、[1, "a", {}] のような異なる型の混在リストが SNBT で書ける。
これは List<Compound> へ暗黙変換され、各要素が
{"": <値>} のような包み方ではなく、Minecraft 側の規則に従って正規化される。
本ライブラリはバイナリ NBT の制約(リストは単一型)を優先し、
異種リストをパースした場合は MALFORMED_DATA を返す。
コマンド文字列の解釈まで担うライブラリではないため、
バイナリへ写せない表現は受理しない(→ adr/0006)。
Snbt.parse(text) -> NbtTag
Snbt.write(tag) -> String -- 1行。空白なし
Snbt.write_pretty(tag) -> String -- インデント 4、要素ごとに改行
出力規則:
Compoundのキーは、bare_stringとして書ける場合は引用符なし。それ以外は"で囲む- キーの順序は挿入順(ソートしない)
Float/Doubleは下記の正準10進表記に接尾辞を付ける(1.0f/1.0d)Doubleは接尾辞dを必ず付ける(省略すると読み戻し時に型が同じでも意図が読み取りにくいため)- 特殊値は
Infinityd/-Infinityd/NaNd(Floatなら接尾辞f) - 型付き配列の要素は接尾辞付きで書く(
[B; 1b, 2b]/[L; 1L])。IntArrayのみ接尾辞なし
型付き配列のパースでは、要素が Byte / Short / Int / Long のいずれでも受理し、
対象の幅に収まるか検査したうえで格納する。Minecraft 自身が
[I; 1, 2] のように接尾辞なしで書き出すため、厳格にすると実データを読めない。
範囲外の値は MALFORMED_DATA。
パースして出力し直した結果が入力と一致することは保証しない(空白や引用の揺れがあるため)。 保証するのは「SNBT → NBT → SNBT → NBT」で NBT が一致することである。
各言語の標準の数値書式(C# の "R"、Java の Float.toString、Python の repr、
Rust の {})は互いに一致しない。指数表記へ切り替わる閾値も、指数部の桁数も、
E の大文字小文字も処理系ごとに違う。
そのままでは SNBT 出力の言語間一致が成立しないため、次の手順を仕様として固定する。
canonical_decimal(値 v, f32 か f64 か):
1. 有効数字 p を 1 から順に増やす(f32 は 9 まで、f64 は 17 まで)
s = v を「有効数字 p 桁の指数表記」で書式化したもの
s を同じ幅の浮動小数点として読み戻し、ビットパターンが v と一致したら採用
2. 採用した s から、仮数の数字列 digits(末尾のゼロを除去)と
10進指数 exp10 を取り出す(v = 0.digits × 10^(exp10+1) ではなく
v = digits[0].digits[1..] × 10^exp10 と読む)
3. -4 <= exp10 <= 16 なら固定小数点表記、そうでなければ指数表記にする
固定小数点表記:
exp10 >= 0 -> 整数部 = digits の先頭 (exp10+1) 桁(足りなければ 0 で右詰め)
小数部 = 残りの桁。無ければ "0"
exp10 < 0 -> "0." + ("0" × (-exp10 - 1)) + digits
指数表記:
digits[0] + "." + (digits[1..] があればそれ、無ければ "0") + "E" + exp10
- 小数部は必ず 1 桁以上書く(
1ではなく1.0)。接尾辞なしでも小数と分かるようにするため - 指数記号は大文字
E、指数はゼロ詰めしない(1.0E20、1.0E-30) - 負値は先頭に
-を付ける。-0.0は-0.0と書く - 特殊値は
Infinity/-Infinity/NaN
例:
| 値 | f32 の出力 | f64 の出力 |
|---|---|---|
| 1 | 1.0f |
1.0d |
| 0.75 | 0.75f |
0.75d |
| 0.49823147 | 0.49823147f |
— |
| 2000 | 2000.0f |
2000.0d |
| 0.015 | — | 0.015d |
| 1e20 | 1.0E20f |
1.0E20d |