.toml 配置文件格式

一、TOML 是什么?

TOML (Tom's Obvious, Minimal Language)是一种为人类阅读优化 的配置文件格式。它的设计目标只有一句话:写起来像 INI 一样直观,读起来比 JSON 更友好,结构上比 YAML 更不容易踩坑。 如果你写过 Rust 的 Cargo.toml、Python 的 pyproject.toml、Hugo 的 config.toml,那么你已经在用它了。近几年 TOML 几乎成为新一代工具链的"事实标准配置格式"。

JSON、YAML、INI 都有一定的缺点,TOML 在这三者之间找到了平衡点:规则少、类型明确、对人友好、对机器无歧义

格式 痛点
JSON 不能写注释;逗号、引号严苛;不适合人写
YAML 缩进敏感;同一份文件可能被解析成多种含义;规范过于复杂
INI 没有统一规范;不支持嵌套、数组、类型

二、文件基本约定

  • 文件后缀:.toml
  • 编码:必须是 UTF-8
  • 大小写敏感
  • 行尾换行符 LF (\n) 或 CRLF (\r\n) 都允许
  • 空白与缩进无意义(这是和 YAML 最大的区别)
  • 注释以 # 开头,可写在行首或行尾
toml 复制代码
# 这是一行注释
title = "TOML 示例"  # 行尾注释也允许

三、键值对(Key/Value)

TOML 的最小单位是键值对

toml 复制代码
key = "value"

规则:

  1. 一行只能写一个键值对
  2. 等号两侧的空格可有可无
  3. 值必须是合法的 TOML 类型(下文详述)
  4. 每个键必须有值------没有"裸键"或"空值"这种概念

3.1 键名(Keys)的三种写法

toml 复制代码
# 1. 裸键(Bare Key):只允许 A-Z a-z 0-9 _ -
name = "Alice"
user_id = 42
api-key = "xxx"

# 2. 字符串键(Quoted Key):用 " 或 ' 包起来,可以包含任意字符
"127.0.0.1" = "localhost"
"中文键" = "也合法"
'literal key' = "原样字符串作键名"

# 3. 点分键(Dotted Key):用 . 创建嵌套结构,等价于嵌套表
server.host = "localhost"
server.port = 8080
# 等价于:
# [server]
# host = "localhost"
# port = 8080

⚠️ 注意:同一个键不能在同一作用域里被重复定义,否则解析器报错。


四、字符串(String)

TOML 提供 4 种字符串,对应"是否多行"和"是否转义"两个维度:

单行 多行
基本(支持转义) "..." """..."""
字面量(原样) '...' '''...'''

4.1 基本字符串

用双引号包围,支持常见转义字符:

toml 复制代码
greeting = "Hello\tWorld\n"
path = "C:\\Users\\Alice"
emoji = "❤"            # ❤

支持的转义序列:\b \t \n \f \r \" \\ \uXXXX \UXXXXXXXX

4.2 多行基本字符串

toml 复制代码
description = """
这是一段
多行文本,
开头的换行会被自动去掉。\
反斜杠+换行可以续行,\
所以这一段最终是一行。
"""

4.3 字面量字符串(推荐写正则、Windows 路径)

用单引号,不解释任何转义

toml 复制代码
regex = '\d{3}-\d{4}'              # 不需要写成 '\\d{3}-\\d{4}'
windows_path = 'C:\Users\Alice'    # 反斜杠原样保留

4.4 多行字面量字符串

toml 复制代码
sql = '''
SELECT *
FROM users
WHERE name = 'Alice'
'''

五、数字与布尔

5.1 整数(Integer)

toml 复制代码
int1 = 99
int2 = -17
int3 = +42
int4 = 1_000_000           # 下划线分隔,提升可读性
hex  = 0xDEADBEEF
oct  = 0o755
bin  = 0b11010110

5.2 浮点数(Float)

toml 复制代码
flt1 = 3.14
flt2 = -0.001
flt3 = 1e10
flt4 = 6.626e-34
flt5 = 1_000.123_456       # 同样支持下划线分隔

# 特殊值
inf  = inf
ninf = -inf
nan  = nan

5.3 布尔(Boolean)

只有两个值,必须小写

toml 复制代码
enabled = true
debug   = false

六、日期与时间

TOML 是少数原生支持日期类型的配置格式,遵循 RFC 3339:

toml 复制代码
# 1. 带时区的日期时间(Offset Date-Time)
dt1 = 2026-05-08T14:30:00+08:00
dt2 = 2026-05-08T06:30:00Z         # Z 表示 UTC

# 2. 本地日期时间(Local Date-Time, 不带时区)
dt3 = 2026-05-08T14:30:00

# 3. 仅日期(Local Date)
d   = 2026-05-08

# 4. 仅时间(Local Time)
t   = 14:30:00.123

不需要任何引号包围------这是 TOML 与 JSON/YAML 最显著的差异之一。


七、数组(Array)

数组用方括号 [],元素间逗号分隔:

toml 复制代码
ports     = [80, 443, 8080]
hosts     = ["alpha", "beta", "gamma"]
mixed_ok  = [1, 2, 3]                 # 推荐:同质类型
mixed_too = [1, "two", 3.0, true]     # 也允许:TOML 1.0 起放宽了限制

# 多行数组,允许尾随逗号
contributors = [
  "Alice <alice@example.com>",
  "Bob <bob@example.com>",
  "Carol <carol@example.com>",   # 尾随逗号合法
]

# 嵌套数组
matrix = [[1, 2], [3, 4], [5, 6]]

八、表(Table):TOML 的灵魂

表(Table) 就是其他语言里的对象、字典、Map。它是 TOML 表达嵌套结构的核心机制。

8.1 标准表

[表名] 声明,之后所有键值对都属于这个表,直到下一个表头出现:

toml 复制代码
title = "全局键放在最前面"

[database]
host = "localhost"
port = 5432
user = "admin"

[server]
bind = "0.0.0.0"
port = 8080

等价于 JSON:

json 复制代码
{
  "title": "全局键放在最前面",
  "database": { "host": "localhost", "port": 5432, "user": "admin" },
  "server":   { "bind": "0.0.0.0", "port": 8080 }
}

💡 最佳实践:所有"裸键值对"都写在文件最顶部,表头之后的内容全部归属对应表。如果把全局键写在表头下面,会被错误地划进那个表。

8.2 嵌套表(点分表名)

toml 复制代码
[servers.alpha]
ip = "10.0.0.1"
role = "frontend"

[servers.beta]
ip = "10.0.0.2"
role = "backend"

等价于:

json 复制代码
{
  "servers": {
    "alpha": { "ip": "10.0.0.1", "role": "frontend" },
    "beta":  { "ip": "10.0.0.2", "role": "backend"  }
  }
}

8.3 内联表(Inline Table)

适合短小、扁平的对象,类似 JSON 风格:

toml 复制代码
point   = { x = 1, y = 2 }
person  = { name = "Alice", age = 30 }

# 内联表必须写在一行内,不能跨行,且一旦写完就"封闭"------
# 不能再用 person.email = "..." 往里面追加键

等价于:

json 复制代码
{
  "point":  { "x": 1, "y": 2 },
  "person": { "name": "Alice", "age": 30 }
}

九、表数组(Array of Tables)

要表达"一组同构对象"(比如多个用户、多个依赖、多个产品),用双方括号 [[...]]

toml 复制代码
[[products]]
name  = "Hammer"
sku   = 738594937

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

[[products]]
name  = "Saw"
sku   = 192847465

等价于:

json 复制代码
{
  "products": [
    { "name": "Hammer", "sku": 738594937 },
    { "name": "Nail",   "sku": 284758393, "color": "gray" },
    { "name": "Saw",    "sku": 192847465 }
  ]
}

每出现一次 [[products]],就向 products 数组里追加一个新元素。

表数组里的子表

toml 复制代码
[[users]]
name = "Alice"

  [users.address]
  city = "Beijing"
  zip  = "100000"

[[users]]
name = "Bob"

  [users.address]
  city = "Shanghai"
  zip  = "200000"

等价于:

json 复制代码
{
  "users": [
    {
      "name": "Alice",
      "address": { "city": "Beijing",  "zip": "100000" }
    },
    {
      "name": "Bob",
      "address": { "city": "Shanghai", "zip": "200000" }
    }
  ]
}

子表的缩进仅为可读性 ,TOML 不要求缩进。但子表头必须写在对应的 [[users]] 之后、下一个 [[users]] 之前,否则归属会错。


十、一份"贴近实战"的完整示例

toml 复制代码
# ===========================
#  myapp.toml --- 应用配置示例
# ===========================

title   = "MyApp"
version = "1.2.0"

[owner]
name = "Alice Wang"
dob  = 1990-07-15T09:00:00+08:00

[database]
host             = "127.0.0.1"
port             = 5432
connection_max   = 100
enabled          = true
allowed_ips      = ["10.0.0.1", "10.0.0.2", "10.0.0.3"]

[database.credentials]
user     = "admin"
password = "s3cret"

[logging]
level   = "info"
outputs = ["stdout", "file"]
file    = { path = "/var/log/myapp.log", max_size_mb = 100 }

[[features]]
name    = "dark-mode"
enabled = true

[[features]]
name    = "beta-search"
enabled = false
rollout = 0.05

[[servers]]
name = "alpha"
ip   = "10.0.0.1"

[[servers]]
name = "beta"
ip   = "10.0.0.2"

十一、容易踩的 5 个坑

1. 全局键被误归到表里

toml 复制代码
[server]
host = "localhost"

title = "MyApp"        # ❌ 这个 title 会被解析成 server.title

✅ 修复:把全局键移到所有表头之上。

2. 重复定义同一张表

toml 复制代码
[server]
host = "a"

[server]               # ❌ 报错:server 被定义两次
host = "b"

✅ TOML 禁止重复定义同名标准表。如果想要多个,用 [[...]] 表数组。

3. 内联表不可"事后追加"

toml 复制代码
point = { x = 1, y = 2 }
point.z = 3            # ❌ 报错:内联表声明后即封闭

✅ 修复:要么写完整 { x = 1, y = 2, z = 3 },要么改用标准表 [point]

4. 字符串混淆

toml 复制代码
path = "C:\Users\Alice"   # ❌ \U 会被当作 Unicode 转义,通常报错

✅ 修复:用字面量字符串 'C:\Users\Alice',或转义 "C:\\Users\\Alice"

5. 日期不要加引号

toml 复制代码
created = "2026-05-08"    # ⚠️ 这是字符串,不是日期
created = 2026-05-08      # ✅ 这才是 TOML 的 Local Date 类型

如果下游程序按"日期"类型读取,引号版本会拿到字符串,导致类型错误。


十二、TOML vs JSON vs YAML 速查

维度 TOML JSON YAML
注释 # #
缩进敏感 ✅(最大坑点)
原生日期类型 ✅(但跨实现不一致)
数字下划线分隔 1_000_000
多种字符串语法 ✅(4 种) ❌(仅一种) ✅(多种)
跨实现一致性 低(同样文本可能解析出 yes/no/true/false)
适合规模 中小型配置 数据交换 复杂层次配置
学习曲线 半小时上手 几分钟 看似简单实际坑多

经验法则机器之间传数据用 JSON;人写配置用 TOML;只有当你确实需要锚点、引用、复杂结构时才用 YAML。


十三、各语言的 TOML 库

语言 推荐库
Rust 标准生态:toml crate
Python 3.11+ 内置 tomllib(只读);写入用 tomli-wtomlkit
Go github.com/BurntSushi/tomlgithub.com/pelletier/go-toml/v2
Node.js / TypeScript @iarna/tomlsmol-toml
Java tomlj
C/C++ tomlplusplus

完整列表见官方维护的清单:https://github.com/toml-lang/toml/wiki

十四、写在最后

TOML 的设计哲学可以浓缩成一句话:

最少的规则、最明确的类型、最不挑剔的人。

它不会取代 JSON 在数据交换里的位置,也不会取代 YAML 在 Kubernetes、Ansible 里的生态。但只要你在写"人会反复打开来手动改的配置文件",TOML 几乎都是更省心的那个选择。

下次新建 config.json 之前,不妨先想想:要不要换成 config.toml

相关推荐
gz927cool1 小时前
【系统架构】可观测性设计及其应用——面向智能体开发视角
人工智能·学习·ai·系统架构
测试开发Kevin1 小时前
梳理OpenAI官方发布的Prompt Engineering指南以及相关最佳实践后总结出的六点核心原则
人工智能·ai·prompt
dblens 数据库管理和开发工具1 小时前
我给 MariaDB 装了个“副驾驶”:DBLens for MariaDB
数据库·ai·mariadb
码途漫谈1 小时前
Easy-Vibe高级开发篇阅读笔记(十三)——多平台开发之Android App 原生开发
android·人工智能·笔记·ai·开源·ai编程
UMI赋能企业2 小时前
优秘智能产品迭代路径:数字人→矩阵系统→营销智脑 V6
ai
杨浦老苏2 小时前
本地AI图像处理工具SnapOtter
docker·ai·工具·群晖·图片
AIGC大时代2 小时前
AI 服务器拉动半导体周期:HBM、DRAM 和消费电子不是同一条曲线
科技·ai·科普·codex·aiwritepaper
摆烂z2 小时前
ai控制本地电脑执行可视化操作
ai