v1.0.0
发布于 2018年7月10日 – 文本版本

TOML v0.5.0

Tom's Obvious, Minimal Language.(汤姆的显而易见、最简语言)

作者:Tom Preston-Werner。

从 0.5.0 版本开始,TOML 应该被认为是极其稳定的。目标是使 1.0.0 版本与 0.5.0 版本向后兼容(在人类能力范围内)。强烈建议所有实现都兼容 0.5.0 版本,以便在 1.0.0 版本发布时,转换过程能够变得简单。

目标

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 文档。
  • 空白字符表示制表符 (0x09) 或空格 (0x20)。
  • 换行符表示 LF (0x0A) 或 CRLF (0x0D0A)。

注释

哈希符号 (#) 将该行其余部分标记为注释。

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

键值对

TOML 文档的主要构建块是键值对。

键位于等号左侧,值位于等号右侧。键名和值周围的空格会被忽略。键、等号和值必须位于同一行(尽管某些值可以跨多行)。

key = "value"

值必须是以下类型之一:字符串、整数、浮点数、布尔值、日期时间、数组或内联表。未指定的类型是无效的。

key = # 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"

只要键尚未直接定义,您仍然可以向其写入数据,也可以向其内部的名称写入数据。

a.b.c = 1
a.d = 2
# THIS IS INVALID
a.b = 1
a.b.c = 2

字符串

有四种方法可以表达字符串:基本字符串、多行基本字符串、字面字符串和多行字面字符串。所有字符串都必须仅包含有效的 UTF-8 字符。

**基本字符串**用双引号括起来。可以使用任何 Unicode 字符,但以下字符除外:双引号、反斜杠和控制字符 (U+0000 到 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)

任何 Unicode 字符都可以使用 \uXXXX\UXXXXXXXX 形式进行转义。转义代码必须是有效的 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+001F、U+007F)。双引号不需要转义,除非它们的出现会导致创建过早的结束分隔符。

如果您经常指定 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.
'''

字面字符串中不允许使用除制表符以外的控制字符。因此,对于二进制数据,建议您使用 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 = 1e6
flt6 = -2E-2

# both
flt7 = 6.626e-34

小数部分是小数点后跟一个或多个数字。

指数部分是 E(大写或小写)后跟整数部分(遵循与整数相同规则)。

与整数类似,您可以使用下划线来提高可读性。每个下划线两侧都必须至少有一个数字。

flt8 = 9_224_617.445_991_228_313

浮点值 -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

分数秒的精度是特定于实现的,但至少应为毫秒精度。如果该值包含超出实现支持的精度,则必须截断多余的精度,而不是四舍五入。

数组

数组是用方括号括起来的值的集合。空格会被忽略。元素用逗号分隔。数据类型不能混合(定义字符串的不同方式应被视为相同类型,数组中的不同元素类型也应被视为相同类型)。

arr1 = [ 1, 2, 3 ]
arr2 = [ "red", "yellow", "green" ]
arr3 = [ [ 1, 2 ], [3, 4, 5] ]
arr4 = [ "all", 'strings', """are the same""", '''type''']
arr5 = [ [ 1, 2 ], ["a", "b", "c"] ]

arr6 = [ 1, 2.0 ] # INVALID

数组也可以是多行的。在数组的最后一个值后,允许使用终止逗号(也称为尾随逗号)。在值之前和结束括号之前,可以有任意数量的换行符和注释。

arr7 = [
  1, 2, 3
]

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

表(也称为哈希表或字典)是键值对的集合。它们单独出现在一行上的方括号中。您可以将它们与数组区分开来,因为数组永远只是值。

[table]

在表下方,以及直到下一个表或文件末尾,都是该表的键值对。表中的键值对不保证按任何特定顺序排列。

[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

允许使用空表,并且它们只是没有键值对。

与键类似,您不能多次定义任何表。这样做是无效的。

# DO NOT DO THIS

[a]
b = 1

[a]
c = 2
# DO NOT DO THIS EITHER

[a]
b = 1

[a.b]
c = 2

内联表

内联表提供了更紧凑的语法来表达表。它们对于可以快速变得冗长的分组数据特别有用。内联表用花括号 {} 括起来。在大括号内,可以出现零个或多个用逗号分隔的键值对。键值对采用与标准表中的键值对相同的形式。允许所有值类型,包括内联表。

内联表旨在出现在单行上。在大括号之间不允许出现换行符,除非它们在值内有效。即使如此,也强烈建议不要将内联表拆分成多行。如果您发现自己有这种需求,则意味着您应该使用标准表。

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"

表数组

尚未表达的最后一种类型是表数组。这些可以通过在表名称中使用双括号来表达。每个具有相同双括号名称的表都将是数组中的一个元素。表按遇到的顺序插入。没有键值对的双括号表将被视为空表。

[[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]
    color = "red"
    shape = "round"

  [[fruit.variety]]
    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 = []

[[fruit]] # Not allowed

尝试使用与已建立数组相同的名称定义普通表,必须在解析时产生错误。

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

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

  # This table conflicts with the previous table
  [fruit.variety]
    name = "granny smith"

您也可以在适当的地方使用内联表

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 与 JSON 非常相似:简单、规范良好,并且可以轻松映射到普遍的数据类型。JSON 非常适合序列化主要由计算机程序读取和写入的数据。TOML 与 JSON 的区别在于它强调易于人类阅读和编写。注释就是一个很好的例子:当数据从一个程序发送到另一个程序时,它们没有任何作用,但在可能由人工编辑的配置文件中非常有用。

YAML 格式面向配置文件,就像 TOML 一样。但是,对于许多用途来说,YAML 是一种过于复杂的解决方案。TOML 旨在追求简单性,而 YAML 规范中并没有体现这一点:http://www.yaml.org/spec/1.2/spec.html

INI 格式也经常用于配置文件。但是,该格式没有标准化,通常无法处理超过一到两级的嵌套。

参与进来

欢迎提供文档、错误报告、拉取请求以及所有其他贡献!

维基

我们有一个 TOML 官方 Wiki,其中编录了以下内容

  • 使用 TOML 的项目
  • 实现
  • 验证器
  • TOML 解码器和编码器的语言无关测试套件
  • 编辑器支持
  • 编码器
  • 转换器

如果您想查看或添加到该列表中,请查看。感谢您成为 TOML 社区的一员!