TOML 配置文件简明教程

今天我们一起来学习toml这种简洁的配置文件格式吧。

TOML是在 2013 年发布的配置文件格式。距今虽然有8年历史了,但是之前一直没有在大项目中见过的。我是前段时间在看python的pep规范,无意中看到了这种配置文件格式,稍微了解了一下,才发现现在有很多新的明星项目都喜欢用它来做配置文件。

1、有rust语言的包管理工具cargo;

2、类似于docker的容器工具containerd;

3、go语言很多项目都喜欢用。比如静态网站生成工具Hugo、数据库InfluxDB、GitLab CI。

4、python项目也开始接纳。包管理工具 pip、pipenv和Poetry都是用的TOML,[1]现在连官方的metadata管理也开始支持toml。

toml-metadata.png

1. 一个小例子

为什么新项目这么喜欢 TOML 呢? 让我们先来看一个TOML的小例子吧,看完了说不定你就知道了。

[project]
name = "spam"
version = "2020.0.0"
description = "Lovely Spam! Wonderful Spam!"
readme = "README.rst"
requires-python = ">=3.8"
license = {file = "LICENSE.txt"}
keywords = ["egg", "bacon", "sausage"]

有没有感觉很熟悉呢?它和ini格式的配置文件几乎一模一样。要知道互联网发展到今天,大多数有影响力的项目都是采用ini这种配置文件格式,如果你之前有接触过ini,切换到toml几乎是0成本。

那为什么不直接用ini呢?因为ini格式支持的数据类型非常少,配置项的值都会被默认当成字符串,如果想表示数字、数组,还需要自己进行额外的解析。

但是在TOML中,数据类型非常丰富:

  • 字符串
  • 注释
  • 数字
  • 布尔值
  • 数组
  • 哈希表
  • 甚至连时间格式都支持

2. 字符串

字符串我们当然不会陌生,但是如果你是从ini格式转过来的,一定要注意字符串值要加引号。

键默认会当成字符串处理,可以加也可以不加。 加引号的键叫引号键(Quoted Key),不加引号的键叫裸键(Bare Key)。

TOML 格式非常灵活,对于灵活的语言,最好的方式是给自己设置规范和纪律,一直用好一种用法,不然很容易学废。官方推荐我们尽量用裸键,当裸键不能正确表达意思的时候,再用引号键。

name = "spam"     #OK
name_1 = spam     #错误
"name_2" = "spam" #OK

裸键只能包含 ASCII 字母,ASCII 数字,下划线和短横线(A-Za-z0-9_-),引号键则可以支持特殊字符,甚至是空格。当键中包含特殊字符的时候,使用引号键。

'name@! erf:' = 'spam' # OK
name@! erf: = 'spam'   # 错误

当字符串为多行字符串时使用三引号,会自动换行:

name = """
this is a
new line 
command"""

得到的结果是:

this is a
new line
command

如果不想自动换行,只是想增强可读性,可以使用折行符号\

name = """
this is a \
new line \
command"""

得到结果是不换行的:

this is a new line command

3. 数字

如果你都会编程了,数字应该会知道怎么表示。

age1 = 18
age2 = -2
age3 = +16
age4 = 11.2

toml除了支持10进制表示,还支持二进制(0b)、八进制(0o)、十六进制(0x)

age1 = 0b11
age2 = 0o12
age3 = 0xb

上面的解析结果为:

{
    'age1': 3, 
 	'age2': 10, 
 	'age3': 11
}

4. 布尔

bool1 = true
bool2 = false

5. 时间

时间类型是toml的一大特色,因为绝大多数的配置文件格式都是不支持时间类型的。toml 遵循的是 RFC 3339 时间格式,只要照着格式写,解析出来会自动转成编程语言的时间类型。

ts = 2021-01-06 07:30:00
ts1 = 2021-01-06 07:30:00.1234
time1 = 07:30:00

得到的结果:

{
    'ts': datetime.datetime(2021, 1, 6, 7, 30), 
 	'ts1': datetime.datetime(2021, 1, 6, 7, 30, 0, 123000),
    'time1': datetime.time(7, 30)
}

6. 数组

数组可以是同类型数据,可以换行,也可以嵌套,和python当中列表简直一模一样。但是要注意,尽量保持元素是同类型的数据

users = [
    ['iswy', '男'],
    ['you', '女'],
]

得到结果:

{
	'users': [
		['iswy', '男'], 
		['you', '女']
	]
}

7. 哈希表

表有点类似于python中的字典:

user = {name='iswy', gender='男'}

得到的结果:

{
    'user': {
        'gender': '男', 
        'name': 'iswy'
    }
}

这种写在key后面的表叫内联表。还有一种用法,类似于ini格式中的section,用于分组管理,暂且把它叫做外联表。比如上面的例子,可以换一种形式,得到的结果是完全一样的。

[user]
gender = '男'
name = 'iswy'

表支持嵌套,子表可以缩进也可以不缩进:

[user]
    cat = ['admin', 'normal']

    [user.student]
    name='iswy'
    age=19

    [user.teacher]
    name='you'
    age=32

如果你觉得这种风格你不喜欢,也可以换成下面的风格:

[user]
cat = ['admin', 'normal']

# student
student.name='iswy'
student.age=19

# teacher
teacher.name='you'
teacher.age=328

一定要注意,当键包含. 号时,一定要加引号

site.'x.com' = 'http://x.com'

我个人更倾向于子表嵌套的风格,因为这样的表述看起来更加简洁。

表和数组的配套使用

最后我们要考虑的问题是当有多个用户需要配置,我们要使用表数组进行分组呢?

[[user]]

    [user.student]
    name='iswy'
    age=19

    [user.teacher]
    name='you'
    age=328

[[user]]
    [user.student]
    name='iswy2'
    age=192

    [user.teacher]
    name='you2'
    age=3282

得到的结果:

{
    'user': [
        {
            'student': {'age': 19, 'name': 'iswy'},
         	'teacher': {'age': 328, 'name': 'you'}
        },
        {
            'student': {'age': 192, 'name': 'iswy2'},
            'teacher': {'age': 3282, 'name': 'you2'}
        }
    ]
}

使用 python 解析

# 安装导入
import toml

with open('demo.toml', encoding='utf-8') as f:
    data = toml.loads(f.read())

  1. https://github.com/toml-lang/toml/wiki#projects-using-toml ↩︎