原文地址:https://aofei.org/posts/2017-03-20-toml
TOML 語法規范
2017-03-20 16:45:58
TOML
全稱:Tom’s Obvious, Minimal Language
作者:Tom Preston-Werner
最新版本:v0.4.0
注意,這個規范還持續變動中,因此你必須時刻都假定着它是一個不穩定的規范並對此做出一些防范措施直到它正式發布了 1.0 版。
目標
TOML 的目標是成為一個擁有明顯語義且容易閱讀的最小化的配置文件格式。TOML 被設計成可以無歧義地被映射為哈希表,從而可以很容易地被解析成各種語言中的數據結構。
例子
-
# 這是一個 TOML 的文檔。
-
-
title = "TOML 例子"
-
-
[owner]
-
name = "Tom Preston-Werner"
-
dob = 1979-05-27T07:32:00-08:00 # 日期是一等公民
-
-
[database]
-
server = "192.168.1.1"
-
ports = [ 8001, 8001, 8002 ]
-
connection_max = 5000
-
enabled = true
-
-
[servers]
-
-
# 縮進(制表符和(或)空格)是允許的,但不是必需的
-
[servers.alpha]
-
ip = "10.0.0.1"
-
dc = "eqdc10"
-
-
[servers.beta]
-
ip = "10.0.0.2"
-
dc = "eqdc10"
-
-
[clients]
-
data = [ ["gamma", "delta"], [1, 2] ]
-
-
# 在數組內部換行是允許的
-
hosts = [
-
"alpha",
-
"omega"
-
]
規范
- TOML 文檔是大小寫敏感的。
- TOML 文檔必須是一個有效的
UTF-8
編碼的Unicode
文檔。 - 空白符只能是制表符(
0x09
)或空格(0x20
)。 - 換行符只能是 LF(
0x0A
)或 CRLF (0x0D0A
)。
注釋
#
將該行的其余部分標記為注釋。
-
# 這是一個占據整行的注釋
-
key = "value" # 這是一個在行末的注釋
鍵值對
一個 TOML 文檔的主要構建塊是鍵值對。
鍵在 =
的左邊,值在右邊。鍵和值周圍的空白符忽略不計。鍵、=
和值必須位於同一行(雖然一些值可以被折成多行)。
key = "value"
鍵可以是裸露的裸鍵,也可以是被包裹在一對 "
的內部的引用鍵。裸鍵只能存在字母、數字、下划線和破折號(a-zA-Z0-9_-
)。注意,裸鍵可以只由數字組成,例如 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" # 非法
-
"" = "blank" # 合法但不可見
-
'' = 'blank' # 合法但不可見
值可以是以下類型:字符串、整數、浮點數、布爾值、時間、數組或內聯表。未確定的值是非法的。
key = # 非法
字符串
字符串的表示方法有四種:基本字符串,多行基本字符串,字面字符串,多行字面字符串。所有字符串都必須只包含有效的 UTF-8
字符。
基本字符串被包裹在一對 "
的內部。任何的 Unicode
字符都可以直接使用,除了那些必須被轉義的字符:"
,\
和控制字符(U+0000
到 U+001F
)。
str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
為了方便起見,一些流行的字符都有簡短的轉義序列。
-
\b - 退格鍵 (U+0008)
-
\t - 制表符 (U+0009)
-
\n - 換行符 (U+000A)
-
\f - 換頁符 (U+000C)
-
\r - 回車符 (U+000D)
-
\" - " (U+0022)
-
\\ - \ (U+005C)
-
\uXXXX - Unicode (U+XXXX)
-
\UXXXXXXXX - Unicode (U+XXXXXXXX)
任何的 Unicode
字符都可以通過 \uXXXX
或 \UXXXXXXXX
形式轉義。轉義的編碼必須是有效的標量值。
所有上面未列出的其他的轉義序列都將被保留不能使用,如果使用,則必須在解析時拋出一個錯誤。
有些時候可能需要表示文本的段落(例如:翻譯文件)或想要將一個非常長的字符串折成多行。TOML 完成起來非常容易。多行基本字符串被包裹在有限行兩側的一對 """
的內部。新行起始位置的換行符將會被忽略不計。所有其他的空白符和換行符都將被保留下來。
-
str1 = """
-
Roses are red
-
Violets are blue"""
TOML 解析器必須兼容不同平台的換行符。
-
# 在 Unix 系統上,上面的多行字符串必須和這個一樣:
-
str2 = "Roses are red\nViolets are blue"
-
-
# 在 Windows 系統上,必須和這個一樣:
-
str3 = "Roses are red\r\nViolets are blue"
書寫長字符串時,使用行結束符來避免引入多余的空白符。當行末的字符是 \
時,TOML 將會忽略所有的空白符(包括換行符)直到遇到非空白符或 """
。所有對於基本字符串有效的轉義序列對於多行基本字符串也是有效的。
-
# 以下字符串完全相同:
-
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
)。"
不需要轉義,除非它的存在會導致多行字符串過早結束。
TOML 還支持不含任何轉義的字面字符串。字面字符串被包裹在一對 '
的內部,像基本字符串一樣,它們必須位於同一行。
-
# 所見即所得。
-
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 # 合法但不可見
前導零、十六進制、八進制和二進制都是不允許的。像是“infinity”和“not a number”這種不能表示為一個連續的數字序列的值也是不被允許的。
整數是 64 位的長整型,范圍是:−9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。
浮點數
浮點數由整數部分(與整數相同的規范)和小數部分和(或)指數部分組成。如果小數部分和指數部分同時存在,那么小數部分必須先於指數部分。
-
# 小數
-
flt1 = +1.0
-
flt2 = 3.1415
-
flt3 = -0.01
-
-
# 指數
-
flt4 = 5e+22
-
flt5 = 1e6
-
flt6 = -2E-2
-
-
# 二者
-
flt7 = 6.626e-34
小數部分由一個 .
開始,然后是一個或多個數字組成。
指數部分由一個 E
(大小寫均可)開始,然后是一個整數部分(和整數遵循相同的規范)組成。
與整數類似,浮點數可以使用 _
來增強其可讀性。_
必須被數字包圍。
flt8 = 9_224_617.445_991_228_313
浮點數是 64 位的(雙)精度數。
布爾值
布爾值的用法和在其他語言中的一樣,且必須小寫。
-
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
秒的小數部分的精度至少是毫秒級。如果值的精度遠比實現的要高,那么多余的精度必須被截斷,而不是舍入。
本地日期時間
如果省略 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 ] # 非法
數組還可以被折成多行。所以除了空白符,數組還忽略了 [
和 ]
的內部的換行符和注釋。]
前允許出現 ,
。
-
arr7 = [
-
1, 2, 3
-
]
-
-
arr8 = [
-
1,
-
2, # 這是允許的
-
]
表格
表格(也叫哈希表或字典)是一個鍵值對的集合。它們被包裹在 [
和 ]
的內部且自成一行。表格和數組是不同的,因為數組只有值。
[table]
在此之下,直到下一個表格或 EOF 之前,都是該表格的鍵值對。表格中的鍵值對是無序的。
-
[table-1]
-
key1 = "some string"
-
key2 = 123
-
-
[table-2]
-
key1 = "another string"
-
key2 = 456
.
禁止出現在裸鍵內,因為 .
是用來表示嵌套表的。每個 .
分隔的部分的命名規范與鍵相同。
-
[dog."tater.man"]
-
type = "pug"
轉換成 JSON
結構如下:
{ "dog": { "tater.man": { "type": "pug" } } }
.
周圍的空白符忽略不計,建議不要使用多余的空白符。
-
[a.b.c] # 這樣做最好
-
[ d.e.f ] # 等價於 [d.e.f]
-
[ g . h . i ] # 等價於 [g.h.i]
-
[ j . "ʞ" . 'l' ] # 等價於 [j."ʞ".'l']
如果不想的話,可以不用聲明所有的父表。TOML 知道該如何處理。
-
# [x] 你
-
# [x.y] 不需要
-
# [x.y.z] 這樣
-
[x.y.z.w] # 直接這樣就行
空表是允許的,其中沒有鍵值對。
只要父表沒有被直接定義,而且沒有定義一個特定的鍵,可以繼續寫入:
-
[a.b]
-
c = 1
-
-
[a]
-
d = 2
不能多次定義鍵和表格。這么做是不合法的。
-
# 別這么干!
-
-
[a]
-
b = 1
-
-
[a]
-
c = 2
-
# 也別這么干!
-
-
[a]
-
b = 1
-
-
[a.b]
-
c = 2
表格的名稱必須是非空的。
-
[] # 非法
-
[a.] # 非法
-
[a..b] # 非法
-
[.b] # 非法
-
[.] # 非法
內聯表
內聯表提供了更緊湊語法來表示表格。它們對於分組數據特別有用,否則將很快變得冗長。內聯表被包裹在 {
和 }
的內部。在 {
和 }
的內部,可能會出現零個或多個用 ,
分隔的鍵值對。鍵值對與標准表中的鍵值對形式相同。允許所有的值類型,包括內聯表。
內聯表的目的是出現在單行。{
和 }
內不允許出現換行符,除非它存在於一個值內。即便如此,還是強烈建議不要將一個內聯表折成多行。如果你發現自己被這種欲望所困擾,那意味着你應該使用標准表。
-
name = { first = "Tom", last = "Preston-Werner" }
-
point = { x = 1, y = 2 }
上面的內聯表與下面的標准表完全相同:
-
[name]
-
first = "Tom"
-
last = "Preston-Werner"
-
-
[point]
-
x = 1
-
y = 2
表格數組
最后要介紹的類型是表格數組。表格數組可以通過包裹在 [[
和 ]]
內的表格名來表達。使用相同的雙方括號名稱的表格是同一個數組的元素。表格按照書寫的順序插入。雙方括號表格如果沒有鍵值對,會被當成空表。
-
[[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"
轉換成 JSON
結構如下:
-
{
-
"fruit": [
-
{
-
"name": "apple",
-
"physical": {
-
"color": "red",
-
"shape": "round"
-
},
-
"variety": [
-
{ "name": "red delicious" },
-
{ "name": "granny smith" }
-
]
-
},
-
{
-
"name": "banana",
-
"variety": [
-
{ "name": "plantain" }
-
]
-
}
-
]
-
}
試圖追加到靜態定義的數組中,即使該數組為空或兼容類型,也必須在解析時拋出錯誤。
-
# 非法的 TOML 文檔
-
fruit = []
-
-
[[fruit]] # 不允許
試圖定義一個標准表,且使用已經定義的數組的名稱,則必須在解析時拋出錯誤:
-
# 非法的 TOML 文檔
-
[[fruit]]
-
name = "apple"
-
-
[[fruit.variety]]
-
name = "red delicious"
-
-
# 這個表格和上面的沖突了
-
[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
來作為其擴展名