v1.0.0
於 2020 年 4 月 3 日發布 – 文字版本

TOML v1.0.0-rc.1

Tom 的簡潔明瞭語言。

由 Tom Preston-Werner、Pradyun Gedam 等人撰寫。

目標

TOML 旨在成為一種簡潔的設定檔格式,由於語意明確,因此容易閱讀。TOML 設計為可明確對應至雜湊表。TOML 應易於解析成各種語言中的資料結構。

範例

# This is a TOML document.

title = "TOML Example"

[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00 # First class dates

[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true

[servers]

  # Indentation (tabs and/or spaces) is allowed but not required
  [servers.alpha]
  ip = "10.0.0.1"
  dc = "eqdc10"

  [servers.beta]
  ip = "10.0.0.2"
  dc = "eqdc10"

[clients]
data = [ ["gamma", "delta"], [1, 2] ]

# Line breaks are OK when inside arrays
hosts = [
  "alpha",
  "omega"
]

規格

  • TOML 區分大小寫。
  • TOML 檔案必須是有效的 UTF-8 編碼 Unicode 文件。
  • 空白字元表示 tab (0x09) 或空白 (0x20)。
  • 換行符號表示 LF (0x0A) 或 CRLF (0x0D 0x0A)。

註解

井號符號標記該行剩餘部分為註解,但字串內除外。

# This is a full-line comment
key = "value"  # This is a comment at the end of a line
another = "# This is not a comment"

註解中不允許 tab (U+0000 至 U+0008、U+000A 至 U+001F、U+007F) 以外的控制字元。

鍵/值對

TOML 文件的主要建構區塊是鍵/值對。

鍵位於等號符號左側,值位於右側。鍵名稱和值周圍的空白字元會被忽略。鍵、等號符號和值必須在同一行(儘管有些值可以斷行至多行)。

key = "value"

值必須具有下列其中一種類型。

未指定的值無效。

key = # INVALID

鍵/值對後必須有換行符號。(有關例外情況,請參閱 內嵌表格。)

first = "Tom" last = "Preston-Werner" # INVALID

鍵可以是裸鍵、引號鍵或點分鍵。

裸鍵只能包含 ASCII 字母、ASCII 數字、底線和連字號 (A-Za-z0-9_-)。請注意,裸鍵可以只由 ASCII 數字組成,例如 1234,但始終會被解釋為字串。

key = "value"
bare_key = "value"
bare-key = "value"
1234 = "value"

引號鍵遵循與基本字串或字面字串完全相同的規則,並允許您使用更廣泛的鍵名稱。最佳實務做法是僅在絕對必要時使用裸鍵。

"127.0.0.1" = "value"
"character encoding" = "value"
"ʎǝʞ" = "value"
'key2' = "value"
'quoted "value"' = "value"

裸鍵不能為空,但允許空引號鍵(儘管不建議使用)。

= "no key name"  # INVALID
"" = "blank"     # VALID but discouraged
'' = 'blank'     # VALID but discouraged

點分鍵是由裸鍵或引號鍵組成的序列,並以點號連結。這允許將類似的屬性分組在一起。

name = "Orange"
physical.color = "orange"
physical.shape = "round"
site."google.com" = true

在 JSON 中,會產生以下結構

{
  "name": "Orange",
  "physical": {
    "color": "orange",
    "shape": "round"
  },
  "site": {
    "google.com": true
  }
}

點分隔部分周圍的空白會被忽略,但最佳做法是不使用任何不必要的空白。

多次定義同一個鍵值是無效的。

# DO NOT DO THIS
name = "Tom"
name = "Pradyun"

由於允許裸鍵僅由 ASCII 整數組成,因此可以撰寫看起來像浮點數但實際上是 2 部分點分鍵值的點分鍵值。除非有充分的理由,否則不要這麼做(你可能沒有)。

3.14159 = "pi"

上述 TOML 映射到以下 JSON。

{ "3": { "14159": "pi" } }

只要鍵值尚未直接定義,你仍然可以寫入該鍵值及其名稱。

# This makes the key "fruit" into a table.
fruit.apple.smooth = true

# So then you can add to the table "fruit" like so:
fruit.orange = 2
# THE FOLLOWING IS INVALID

# This defines the value of fruit.apple to be an integer.
fruit.apple = 1

# But then this treats fruit.apple like it's a table.
# You can't turn an integer into a table.
fruit.apple.smooth = true

不建議以不按順序的方式定義點分鍵值。

# VALID BUT DISCOURAGED

apple.type = "fruit"
orange.type = "fruit"

apple.skin = "thin"
orange.skin = "thick"

apple.color = "red"
orange.color = "orange"
# RECOMMENDED

apple.type = "fruit"
apple.skin = "thin"
apple.color = "red"

orange.type = "fruit"
orange.skin = "thick"
orange.color = "orange"

字串

有四種表達字串的方式:基本字串、多行基本字串、字面字串和多行字面字串。所有字串都只能包含有效的 UTF-8 字元。

基本字串以引號括住。可以使用任何 Unicode 字元,但必須跳脫以下字元:引號、反斜線和 tab 以外的控制字元(U+0000 至 U+0008、U+000A 至 U+001F、U+007F)。

str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."

為方便起見,一些常用的字元有簡潔的跳脫序列。

\b         - backspace       (U+0008)
\t         - tab             (U+0009)
\n         - linefeed        (U+000A)
\f         - form feed       (U+000C)
\r         - carriage return (U+000D)
\"         - quote           (U+0022)
\\         - backslash       (U+005C)
\uXXXX     - unicode         (U+XXXX)
\UXXXXXXXX - unicode         (U+XXXXXXXX)

可以使用 \uXXXX\UXXXXXXXX 形式跳脫任何 Unicode 字元。跳脫碼必須是有效的 Unicode 純量值

上述未列出的所有其他跳脫序列都是保留的,如果使用,TOML 應產生錯誤。

有時你需要表達文字段落(例如翻譯檔案)或想將很長的字串分成多行。TOML 讓這變得容易。

多行基本字串兩側都有三個引號,並允許換行。開頭分隔符號後面的換行會被修剪。所有其他空白和換行字元保持不變。

str1 = """
Roses are red
Violets are blue"""

TOML 解析器可以自由地將換行符號標準化為對其平台有意義的任何形式。

# On a Unix system, the above multi-line string will most likely be the same as:
str2 = "Roses are red\nViolets are blue"

# On a Windows system, it will most likely be equivalent to:
str3 = "Roses are red\r\nViolets are blue"

若要撰寫長字串而不引入額外的空白,請使用「換行反斜線」。當一行中最後一個非空白字元為 \ 時,它將與所有空白(包括換行)一起修剪,直到下一個非空白字元或關閉分隔符號。所有對基本字串有效的跳脫序列對多行基本字串也都有效。

# The following strings are byte-for-byte equivalent:
str1 = "The quick brown fox jumps over the lazy dog."

str2 = """
The quick brown \


  fox jumps over \
    the lazy dog."""

str3 = """\
       The quick brown \
       fox jumps over \
       the lazy dog.\
       """

可以使用任何 Unicode 字元,但必須跳脫的字元除外:反斜線和控制字元(製表符、換行符和回車符除外)(U+0000 至 U+0008、U+000B、U+000C、U+000E 至 U+001F、U+007F)。

可以在多行基本字串內的任何位置撰寫引號或兩個相鄰的引號。它們也可以寫在分隔符號內。

str4 = """Here are two quotation marks: "". Simple enough."""
# str5 = """Here are three quotation marks: """."""  # INVALID
str5 = """Here are three quotation marks: ""\"."""
str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""

# "This," she said, "is just a pointless statement."
str7 = """"This," she said, "is just a pointless statement.""""

如果您經常指定 Windows 路徑或正規表示式,那麼必須跳脫反斜線很快就會變得繁瑣且容易出錯。為了提供協助,TOML 支援完全不允許跳脫的字面字串。

字面字串由單引號包圍。與基本字串一樣,它們必須出現在單一行中

# What you see is what you get.
winpath  = 'C:\Users\nodejs\templates'
winpath2 = '\\ServerX\admin$\system32\'
quoted   = 'Tom "Dubs" Preston-Werner'
regex    = '<\i\c*\s*>'

由於沒有跳脫,因此無法在由單引號包圍的字面字串內撰寫單引號。幸運的是,TOML 支援解決此問題的多行版本字面字串。

多行字面字串由兩側的三个單引號包圍,並允許換行。與字面字串一樣,完全沒有跳脫。開頭分隔符號後緊接的換行將被修剪。分隔符號之間的所有其他內容都將按原樣解釋,不作任何修改。

regex2 = '''I [dw]on't need \d{2} apples'''
lines  = '''
The first newline is
trimmed in raw strings.
   All other whitespace
   is preserved.
'''

您可以在多行字面字串中的任何位置撰寫 1 或 2 個單引號,但禁止出現三個或更多個單引號的序列。

quot15 = '''Here are fifteen quotation marks: """""""""""""""'''

# apos15 = '''Here are fifteen apostrophes: ''''''''''''''''''  # INVALID
apos15 = "Here are fifteen apostrophes: '''''''''''''''"

# 'That's still pointless', she said.
str = ''''That's still pointless', she said.'''

字面字串中不允許製表符以外的控制字元。因此,對於二進位資料,建議您使用 Base64 或其他適當的 ASCII 或 UTF-8 編碼。該編碼的處理將取決於應用程式。

整數

整數是整數。正數可以加上正號前綴。負數加上負號前綴。

int1 = +99
int2 = 42
int3 = 0
int4 = -17

對於大數字,您可以在數字之間使用底線來增強可讀性。每個底線兩側都必須至少有一個數字。

int5 = 1_000
int6 = 5_349_221
int7 = 1_2_3_4_5     # VALID but discouraged

不允許前導零。整數值 -0+0 有效,且與未加前綴的零相同。

非負整數值也可以用十六進位、八進位或二進位表示。在這些格式中,不允許前導 +,但允許前導零(在字首之後)。十六進位值不分大小寫。數字之間允許使用底線(但字首和值之間不允許)。

# hexadecimal with prefix `0x`
hex1 = 0xDEADBEEF
hex2 = 0xdeadbeef
hex3 = 0xdead_beef

# octal with prefix `0o`
oct1 = 0o01234567
oct2 = 0o755 # useful for Unix file permissions

# binary with prefix `0b`
bin1 = 0b11010110

預期 64 位元(有號長整數)範圍(−9,223,372,036,854,775,808 至 9,223,372,036,854,775,807)。

浮點數

浮點數應實作為 IEEE 754 binary64 值。

浮點數包含整數部分(遵循與十進位整數值相同的規則),後接小數部分和/或指數部分。如果小數部分和指數部分都存在,則小數部分必須在指數部分之前。

# fractional
flt1 = +1.0
flt2 = 3.1415
flt3 = -0.01

# exponent
flt4 = 5e+22
flt5 = 1e06
flt6 = -2E-2

# both
flt7 = 6.626e-34

小數部分是小數點後接一個或多個數字。

指數部分是 E(大寫或小寫),後接整數部分(遵循與十進位整數值相同的規則,但可能包含前導零)。

類似於整數,您可以使用底線來增強可讀性。每個底線必須至少被一個數字包圍。

flt8 = 224_617.445_991_228

浮點值 -0.0+0.0 有效,且應根據 IEEE 754 進行對應。

也可以表示特殊浮點值。它們總是小寫。

# infinity
sf1 = inf  # positive infinity
sf2 = +inf # positive infinity
sf3 = -inf # negative infinity

# not a number
sf4 = nan  # actual sNaN/qNaN encoding is implementation specific
sf5 = +nan # same as `nan`
sf6 = -nan # valid, actual encoding is implementation specific

布林值

布林值只是您習慣的令牌。總是小寫。

bool1 = true
bool2 = false

偏移日期時間

若要明確表示特定時間點,您可以使用 RFC 3339 格式化的日期時間和偏移量。

odt1 = 1979-05-27T07:32:00Z
odt2 = 1979-05-27T00:32:00-07:00
odt3 = 1979-05-27T00:32:00.999999-07:00

為了可讀性,您可以使用空格取代日期和時間之間的 T 分隔符號(如 RFC 3339 第 5.6 節所允許)。

odt4 = 1979-05-27 07:32:00Z

小數秒的精度取決於實作,但預期至少有毫秒精度。如果值包含的精度高於實作所能支援的精度,則必須截斷額外的精度,而不是四捨五入。

當地日期時間

如果您從 RFC 3339 格式化的日期時間中省略偏移量,它將表示給定的日期時間,而與偏移量或時區無關。在沒有其他資訊的情況下,它無法轉換為時間點。如果需要,轉換為時間點取決於實作。

ldt1 = 1979-05-27T07:32:00
ldt2 = 1979-05-27T00:32:00.999999

小數秒的精度取決於實作,但預期至少有毫秒精度。如果值包含的精度高於實作所能支援的精度,則必須截斷額外的精度,而不是四捨五入。

當地日期

如果您只包含 RFC 3339 格式化日期時間的日期部分,它將表示整個日期,而與偏移量或時區無關。

ld1 = 1979-05-27

當地時間

如果您只包含 RFC 3339 格式化日期時間的時間部分,它將表示該時間,而與特定日期或任何偏移量或時區無關。

lt1 = 07:32:00
lt2 = 00:32:00.999999

小數秒的精度取決於實作,但預期至少有毫秒精度。如果值包含的精度高於實作所能支援的精度,則必須截斷額外的精度,而不是四捨五入。

陣列

陣列是方括號,裡面有值。空白會被忽略。元素以逗號分隔。陣列可以包含與鍵值對中允許的相同資料類型值。可以混合不同類型的值。

integers = [ 1, 2, 3 ]
colors = [ "red", "yellow", "green" ]
nested_array_of_int = [ [ 1, 2 ], [3, 4, 5] ]
nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ]
string_array = [ "all", 'strings', """are the same""", '''type''' ]

# Mixed-type arrays are allowed
numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ]
contributors = [
  "Foo Bar <foo@example.com>",
  { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" }
]

陣列可以跨越多行。在陣列的最後一個值之後,終止逗號(也稱為尾隨逗號)是可以的。在值之前和在結束括號之前,可以有任意數量的換行符和註解。

integers2 = [
  1, 2, 3
]

integers3 = [
  1,
  2, # this is ok
]

表格

表格(也稱為雜湊表或字典)是鍵值對的集合。它們出現在單獨一行的方括號中。您可以將它們與陣列區分開來,因為陣列永遠只會是值。

[table]

在它下方,直到下一個表格或 EOF 是該表格的鍵值。表格中的鍵值對並非保證以任何特定順序排列。

[table-1]
key1 = "some string"
key2 = 123

[table-2]
key1 = "another string"
key2 = 456

表格的命名規則與鍵相同(請參閱上述鍵的定義)。

[dog."tater.man"]
type.name = "pug"

在 JSON 中,會產生以下結構

{ "dog": { "tater.man": { "type": { "name": "pug" } } } }

鍵周圍的空白會被忽略,但最佳做法是不使用任何多餘的空白。

[a.b.c]            # this is best practice
[ d.e.f ]          # same as [d.e.f]
[ g .  h  . i ]    # same as [g.h.i]
[ j . "ʞ" . 'l' ]  # same as [j."ʞ".'l']

如果你不想要,你不需要指定所有超表。TOML 知道如何為你執行此操作。

# [x] you
# [x.y] don't
# [x.y.z] need these
[x.y.z.w] # for this to work

[x] # defining a super-table afterwards is ok

允許空表,並且其中沒有鍵/值對。

與鍵一樣,你無法定義任何表超過一次。這樣做是無效的。

# DO NOT DO THIS

[fruit]
apple = "red"

[fruit]
orange = "orange"
# DO NOT DO THIS EITHER

[fruit]
apple = "red"

[fruit.apple]
texture = "smooth"

不建議定義表格時順序錯亂。

# VALID BUT DISCOURAGED
[fruit.apple]
[animal]
[fruit.orange]
# RECOMMENDED
[fruit.apple]
[fruit.orange]
[animal]

帶點的鍵將每個點左邊的所有內容定義為一個表。由於無法定義表超過一次,因此不允許使用 [table] 標頭重新定義此類表。同樣,不允許使用帶點的鍵重新定義已在 [table] 表單中定義的表。

但是,可以使用 [table] 表單來定義透過帶點的鍵定義的表中的子表。

[fruit]
apple.color = "red"
apple.taste.sweet = true

# [fruit.apple]  # INVALID
# [fruit.apple.taste]  # INVALID

[fruit.apple.texture]  # you can add sub-tables
smooth = true

內嵌表格

內嵌表提供更簡潔的語法來表示表。它們對於分組資料特別有用,否則這些資料可能會很快變得冗長。內嵌表用大括號 {} 括起來。在大括號內,可能會出現零個或多個以逗號分隔的鍵/值對。鍵/值對的形式與標準表中的鍵/值對相同。允許所有值類型,包括內嵌表。

內嵌表旨在出現在單一行上。內嵌表中最後一個鍵/值對之後不允許有終止逗號(也稱為尾隨逗號)。在大括號之間不允許換行,除非它們在值中有效。即便如此,強烈建議不要將內嵌表拆分成多行。如果你發現自己有這種慾望,這表示你應該使用標準表。

name = { first = "Tom", last = "Preston-Werner" }
point = { x = 1, y = 2 }
animal = { type.name = "pug" }

上面的內嵌表與以下標準表定義相同

[name]
first = "Tom"
last = "Preston-Werner"

[point]
x = 1
y = 2

[animal]
type.name = "pug"

內嵌表完全定義了鍵和其中的子表。無法向它們新增鍵和子表。

[product]
type = { name = "Nail" }
# type.edible = false  # INVALID

同樣,內嵌表不能用於向已定義的表新增鍵或子表。

[product]
type.name = "Nail"
# type = { edible = false }  # INVALID

表格陣列

尚未表達的最後一種類型是表陣列。這些可以使用雙括號中的表名稱來表達。在該表之下,直到下一個表或 EOF 是該表的鍵/值。具有相同雙括號名稱的每個表將是表陣列中的元素。表按遇到的順序插入。沒有任何鍵/值對的雙括號表將被視為空表。

[[products]]
name = "Hammer"
sku = 738594937

[[products]]

[[products]]
name = "Nail"
sku = 284758393

color = "gray"

在 JSON 領域中,這將為你提供以下結構。

{
  "products": [
    { "name": "Hammer", "sku": 738594937 },
    { },
    { "name": "Nail", "sku": 284758393, "color": "gray" }
  ]
}

你也可以建立巢狀表陣列。只需在子表上使用相同的雙括號語法。每個雙括號子表都將屬於最新定義的表元素。正常的子表(不是陣列)也屬於最新定義的表元素。

[[fruit]]
  name = "apple"

  [fruit.physical]  # subtable
    color = "red"
    shape = "round"

  [[fruit.variety]]  # nested array of tables
    name = "red delicious"

  [[fruit.variety]]
    name = "granny smith"

[[fruit]]
  name = "banana"

  [[fruit.variety]]
    name = "plantain"

上述 TOML 映射到以下 JSON。

{
  "fruit": [
    {
      "name": "apple",
      "physical": {
        "color": "red",
        "shape": "round"
      },
      "variety": [
        { "name": "red delicious" },
        { "name": "granny smith" }
      ]
    },
    {
      "name": "banana",
      "variety": [
        { "name": "plantain" }
      ]
    }
  ]
}

如果表格或表格陣列的父層是陣列元素,則在定義子層之前,該元素必須已經定義。嘗試反轉該順序,必須在解析時產生錯誤。

# INVALID TOML DOC
[fruit.physical]  # subtable, but to which parent element should it belong?
  color = "red"
  shape = "round"

[[fruit]]  # parser must throw an error upon discovering that "fruit" is
           # an array rather than a table
  name = "apple"

嘗試附加到靜態定義的陣列,即使該陣列是空的或相容的類型,也必須在解析時產生錯誤。

# INVALID TOML DOC
fruit = []

[[fruit]] # Not allowed

嘗試定義與已建立陣列同名的常規表格,必須在解析時產生錯誤。嘗試將常規表格重新定義為陣列,也必須產生解析時錯誤。

# INVALID TOML DOC
[[fruit]]
  name = "apple"

  [[fruit.variety]]
    name = "red delicious"

  # INVALID: This table conflicts with the previous array of tables
  [fruit.variety]
    name = "granny smith"

  [fruit.physical]
    color = "red"
    shape = "round"

  # INVALID: This array of tables conflicts with the previous table
  [[fruit.physical]]
    color = "green"

您也可以在適當的地方使用內嵌表格

points = [ { x = 1, y = 2, z = 3 },
           { x = 7, y = 8, z = 9 },
           { x = 2, y = 4, z = 8 } ]

檔案副檔名

TOML 檔案應使用副檔名 .toml

MIME 類型

透過網際網路傳輸 TOML 檔案時,適當的 MIME 類型為 application/toml

與其他格式的比較

TOML 與用於應用程式設定檔和資料序列化等其他檔案格式(例如 YAML 和 JSON)有共同特徵。TOML 和 JSON 都很簡單,且使用普遍的資料類型,讓機器容易為其編碼或解析。TOML 和 YAML 都強調人類可讀性功能,例如讓了解特定行目的更為容易的註解。TOML 的不同之處在於結合這些功能,允許註解(與 JSON 不同),但保留簡潔性(與 YAML 不同)。

由於 TOML 明確地用作設定檔格式,因此解析它很簡單,但它並非用於序列化任意資料結構。TOML 在檔案的最上層總是有雜湊表,資料可以輕鬆地嵌套在其鍵內,但它不允許最上層陣列或浮點數,因此它無法直接序列化某些資料。也沒有標準識別 TOML 檔案的開始或結束,這可能會使透過串流傳送它變得複雜。這些詳細資訊必須在應用程式層進行協商。

INI 檔案經常與 TOML 相比,因為它們在語法和用作設定檔方面有相似之處。但是,INI 沒有標準化的格式,而且它們無法優雅地處理超過一或兩個層級的巢狀結構。

進一步閱讀

參與貢獻

歡迎提供文件、錯誤報告、拉取請求和所有其他貢獻!

Wiki

我們有一個官方 TOML Wiki,其中分類下列項目

  • 使用 TOML 的專案
  • 實作
  • 驗證器
  • TOM L 解碼器和編碼器的與語言無關的測試套件
  • 編輯器支援
  • 編碼器
  • 轉換器

如果您想檢視或新增到該清單,請查看。感謝您成為 TOML 社群的一份子!