ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

TOML 配置语言完全指南:设计目标、语法全解与多格式对比

TOML 配置语言完全指南:设计目标、语法全解与多格式对比 TOML 配置语言完全指南设计目标、语法全解与多格式对比【免费下载链接】tomlToms Obvious, Minimal Language项目地址: https://gitcode.com/gh_mirrors/to/tomlTOMLToms Obvious, Minimal Language是一款以语义显而易见为第一原则的配置文件格式由 Tom Preston-Werner、Pradyun Gedam 等人共同设计。本文以本仓库中的 README.md 为骨架结合完整的 规范文档 与 ABNF 文法系统讲解 TOML 的设计动机、全部内置数据类型与语法细节、与 JSON/YAML/INI 的定位差异以及规范本身在仓库中的演进与发布方式帮助你写出一眼可读、零歧义、可被任意主流语言直接解析的配置文件。设计目标为什么需要 TOMLTOML 的定位在 README.md 的 Objectives 一节中表达得非常明确它可以概括为三条核心原则最小化TOML 是一种极简的配置文件格式只提供完成配置任务所必需的语言要素不追求序列化任意数据结构的完备性语义明显格式的读写不依赖上下文推断任何一行配置的目的都清晰可见降低人类阅读与维护成本无歧义映射TOML 被设计为可以无歧义地映射到一张哈希表hash table且应当能被多种语言轻松解析成对应的数据结构。这三条原则共同决定了 TOML 的两个重要外观特征文件顶层永远是一张哈希表不存在顶层数组、顶层裸浮点这类结构以及语法规则严格而收敛例如键的拼写形式受限、非法写法在解析期即报错。后者也意味着 TOML 的解析器实现相对简单——这也是规范允许自由采用各语言实现的前提。快速上手一个完整的 TOML 文档README.md 给出了一个覆盖绝大多数语法的官方示例先把它完整保留下来作为后文逐一拆解的素材# 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 [ 8000, 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 ]这个示例在短短的 30 行里展示了 TOML 的几乎所有核心概念示例片段展示的语法点title TOML Example根表中的键值对、基础字符串[owner]、[database]、[servers]、[clients]表Table头[a.b.c]形式的点号路径dob 1979-05-27T07:32:00-08:00带时区偏移的日期时间Offset Date-Timeports [ 8000, 8001, 8002 ]同构数组connection_max 5000整数支持_千分位分隔enabled true布尔值[servers.alpha]与缩进嵌套表缩进被视为空白、可加可不加data [ [gamma, delta], [1, 2] ]嵌套数组、异构元素hosts [ alpha, omega ]跨行数组允许在数组内换行各种#注释行内注释与整行注释将该示例交给任何符合规范的 TOML 解析器得到的结构等价于如下 JSON{ title: TOML Example, owner: { name: Tom Preston-Werner, dob: 1979-05-27T07:32:00-08:00 }, database: { server: 192.168.1.1, ports: [8000, 8001, 8002], connection_max: 5000, enabled: true }, servers: { alpha: { ip: 10.0.0.1, dc: eqdc10 }, beta: { ip: 10.0.0.2, dc: eqdc10 } }, clients: { data: [[gamma, delta], [1, 2]], hosts: [alpha, omega] } }注意到[servers.alpha]与[servers.beta]自动把servers变成了一张子表——这正是点号路径表头的语义TOML 会替你补齐所有中间层级的 super-table。TOML 与 JSON、YAML、INI定位与取舍README.md 用专门的 Comparison 一节说明了 TOML 与其他常见格式的关系这是理解 TOML 设计哲学的关键。与 JSON、YAML 的共性TOML 与 JSON两者都足够简单使用的数据类型普遍存在因而对机器来说都好写、好解析TOML 与 YAML两者都强调人类可读性例如都支持注释便于读者理解每一行的用途。TOML 的差异点取两者之长TOML 的特殊之处在于组合了上述优点像 JSON 一样保持语法精简与类型收敛但支持注释JSON 不支持像 YAML 一样强调可读性但避免了 YAML 语义上的复杂性从而保持简单。TOML 的边界它不是通用序列化格式README 特别提醒了几个容易被误解的边界解析容易但设计用途只是配置TOML 并不打算用于序列化任意数据结构顶层恒为哈希表文件顶层不允许出现裸数组或裸浮点因此某些数据如一个纯数组无法被 TOML 直接序列化没有流式边界标识TOML 文件没有标准化的起始/结束标记通过流传输时需要由应用层自行协商边界。与 INI 的对比INI 文件因语法相似且同为配置文件经常被拿来与 TOML 比较。但 INI没有标准化规范且不能优雅地处理超过一两层的嵌套这正是 TOML 用[a.b.c]表路径、内联表、表数组等机制解决的问题。语法基础注释、键值对与键规范文档 对 README 示例中出现的每一项语法都给出了精确定义以下按主题深入。注释Comment#符号标记该行其余部分为注释但在字符串内部不生效# This is a full-line comment key value # This is a comment at the end of a line another # This is not a comment注释中不允许出现除 tabU0009以外的控制字符U0000 至 U0008、U000A 至 U001F、U007F注释只服务于人类读者解析器绝不能依据注释的存在或内容修改键与值。键值对Key/Value Pair键值对是 TOML 文档的基本单元键在等号左侧值在右侧等号两侧空白被忽略且键、等号、值必须位于同一行部分多行值除外key value未指定值是非法的key # INVALID这种写法会直接报错一个键值对之后必须有换行或文件结束first Tom last Preston-Werner这样的同行双键值对也是非法的。值只能是规范规定的十种类型之一字符串、整数、浮点、布尔、Offset Date-Time、Local Date-Time、Local Date、Local Time、数组、内联表。键Keys裸键、引号键与点号键键有三种写法规范对每种都有严格约束裸键bare keys只能包含 ASCII 字母、数字、下划线和连字符A-Za-z0-9_-。注意纯数字裸键如1234是合法的但永远按字符串解释key value bare_key value bare-key value 1234 value引号键quoted keys遵循基础字符串或字面字符串的规则因此可以承载远更宽泛的键名如包含空格、点号、Unicode 字符最佳实践是能不用就不用127.0.0.1 value character encoding value ʎǝʞ value key2 value quoted value value裸键必须非空空引号键合法但被劝阻不能用多行字符串定义键。点号键dotted keys是用点连接的一串裸键或引号键用于把相关属性归组name Orange physical.color orange physical.shape round site.google.com true点号两侧的空白被忽略但规范建议不加多余空白缩进同样被视为空白。同一键重复定义是非法的且裸键与引号键等价——spelling favorite之后再写spelling favourite会报错。点号键还有一个重要语义只要某个键尚未被直接定义就仍然可以向它及它内部的名称写入从而隐式创建中间表# 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但反过来一旦fruit.apple 1把fruit.apple定义成了整数再写fruit.apple.smooth true就是非法的——不能把整数变成表。字符串的四种形态字符串分为基础、多行基础、字面、多行字面四种且所有字符串只能包含 Unicode 字符。基础字符串Basic Strings用双引号包裹除必须转义的字符外可使用任意 Unicode 字符。规范内置一组紧凑转义序列转义含义码点\b退格 backspaceU0008\t制表符 tabU0009\n换行 linefeedU000A\f换页 form feedU000C\r回车 carriage returnU000D\e转义 escapeU001B\双引号U0022\\反斜杠U005C\xHHUnicode码点 ≤ 0xFFU00HH\uHHHHUnicodeUHHHH\UHHHHHHHHUnicodeUHHHHHHHH示例str Im a string. \You can quote me\. Name\tJos\xE9\nLocation\tSF.\xHH、\uHHHH、\UHHHHHHHH必须转义为合法的 Unicode 标量值scalar value未列出的转义序列一律保留并应产生错误。注意TOML 字符串是 Unicode 字符序列而非字节序列二进制数据应使用十六进制或 Base64 等字节转文本策略而不是塞进转义码。多行基础字符串Multi-line Basic Strings用三引号包裹、允许换行紧跟开始定界符的第一个换行会被裁剪其余空白与换行原样保留。解析器可以把换行规范化为平台习惯的形式Unix 下等同\nWindows 下等同\r\n。配合行尾反斜杠line ending backslash可以写出不引入多余空白的超长字符串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.\ 以上三个字符串字节级等价。规则当一行的最后一个非空白字符是未转义的\时它与后面直到下一个非空白字符或结束定界符的所有空白含换行一并被裁剪。多行基础字符串内可以出现单个或两个连续的双引号包括紧贴定界符内侧但三个及以上需要转义处理。字面字符串Literal Strings用单引号包裹、完全不允许转义所见即所得适合 Windows 路径或正则表达式winpath C:\Users\nodejs\templates winpath2 \\ServerX\admin$\system32\ quoted Tom Dubs Preston-Werner regex \i\c*\s*多行字面字符串Multi-line Literal Strings用三单引号包裹、允许换行、仍无任何转义开头紧跟的第一个换行被裁剪换行规范化规则与多行基础字符串一致字符串内最多允许连续两个单引号三个及以上非法。数值整数、浮点与特殊值整数Integer支持正负号前缀99、42、0、-17可用下划线增强可读性下划线两侧必须都有数字如1_000、5_349_221不允许前导零-0与0合法且等价于0。非负整数还支持十六进制0x、八进制0o、二进制0b前缀十六进制不区分大小写前缀后允许前导零下划线不能出现在前缀与数字之间hex1 0xDEADBEEF oct1 0o01234567 oct2 0o755 # useful for Unix file permissions bin1 0b11010110规范要求实现至少能无损接受并处理 64 位有符号整数−2^63 到 2^63−1无法无损表示时必须报错整数大小不受限制时由实现自行决定。浮点Float浮点 整数部分 小数部分和/或指数部分若两者都有小数部分必须在指数之前# fractional flt1 1.0 flt2 3.1415 flt3 -0.01 # exponent flt4 5e22 flt5 1e06 flt6 -2E-2 # both flt7 6.626e-34小数点两侧必须各至少有一位数字.7、7.、3.e20都是非法浮点小数/指数部分内部也允许下划线224_617.445_991_228。-0.0与0.0合法且按 IEEE 754 映射。特殊值一律小写sf1 inf # positive infinity sf2 inf # positive infinity sf3 -inf # negative infinity sf4 nan # actual sNaN/qNaN encoding is implementation-specific sf5 nan # same as nan sf6 -nan # valid, encoding is implementation-specific规范建议实现至少支持 IEEE 754 binary64 精度。布尔与日期时间布尔Boolean只有两个全小写 tokentrue、false。Offset Date-Time带偏移日期时间用于无歧义地表示某一时刻采用 RFC 3339 格式并带时区偏移odt1 1979-05-27T07:32:00Z odt2 1979-05-27T00:32:00-07:00 odt3 1979-05-27T00:32:00.5-07:00 odt4 1979-05-27T00:32:00.999999-07:00日期与时间之间的分隔符T可替换为空格RFC 3339 第 5.6 节允许秒可以省略省略时按:00处理odt5 1979-05-27 07:32:00Z odt6 1979-05-27 07:32Z odt7 1979-05-27 07:32-07:00实现至少须支持毫秒精度超出支持精度的多余小数位必须截断而不是四舍五入。Local Date-Time / Local Date / Local Time去掉偏移的 RFC 3339 日期时间即为 Local Date-Time它不与任何时区挂钩也无法单独换算成时刻ldt1 1979-05-27T07:32:00 ldt2 1979-05-27T07:32:00.5 ldt3 1979-05-27T00:32:00.999999 ldt4 1979-05-27T07:32只写日期是 Local Dateld1 1979-05-27只写时间是 Local Timelt1 07:32:00 lt2 00:32:00.5 lt3 00:32:00.999999 lt4 07:32Local Date 表示一整天Local Time 表示一天中的某个时刻两者都与时区无关时间秒同样可省略精度规则同上。数组、表与嵌套结构数组Array方括号包围的有序值集合元素用逗号分隔空白被忽略允许混合不同类型的元素这是与早期版本的重要差异见 CHANGELOG.md 中 1.0.0-rc.1 的说明integers [ 1, 2, 3 ] colors [ red, yellow, green ] nested_arrays_of_ints [ [ 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 fooexample.com, { name Baz Qux, email bazquxexample.com, url https://example.com/bazqux } ]数组可以跨多行允许末尾逗号trailing comma值、逗号、闭括号前可以随意出现换行和注释integers3 [ 1, 2, # this is ok ]表Table表即哈希表/字典由独占一行的方括号表头定义表头之下的键值对归属该表直到下一个表头或文件结束表内键值对不保证任何顺序[table-1] key1 some string key2 123 [table-2] key1 another string key2 456表名规则与键相同因此可以写出[dog.tater.man]这种含引号键的表名对应 JSON 结构{ dog: { tater.man: { ... } } }。表头两侧空白被忽略缩进也是空白嵌套深度没有硬性上限但规范建议实现至少支持 100 层以防资源滥用。几个关键语义需要牢记中间 super-table 可省略[x.y.z.w]无需先声明[x]、[x.y]、[x.y.z]之后补写[x]也合法表不能重复定义同一[fruit]出现两次、或在[fruit]下定义了apple red后又写[fruit.apple]均非法根表root table位于文档开头、第一个表头之前或 EOF 之前无名且不可移动点号键与表头的互斥用点号键创建的表不能再用[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内联表Inline Table用花括号{ }提供紧凑的表写法特别适合快速变冗长的分组嵌套数据支持同/异行多个键值对以及末尾逗号name { first Tom, last Preston-Werner } point {x1, y2} animal { type.name pug } contact { personal { name Donald Duck, email donaldduckburg.com, }, work { name Coin cleaner, email donaldScroogeCorp.com, }, }内联表完全自包含花括号外不能再向其中添加键或子表type { name Nail }之后再写type.edible false非法反过来也不能用内联表向已定义表追加内容。数组的表Array of Tables用双括号表头[[product]]声明首次出现定义数组及其第一个元素之后每次出现追加一个新元素元素按出现顺序入数组[[product]] name Hammer sku 738594937 [[product]] # empty table within the array [[product]] name Nail sku 284758393 color gray对应 JSON{ product: [ { name: Hammer, sku: 738594937 }, {}, { name: Nail, sku: 284758393, color: gray } ] }任何对数组的表的引用都指向最近定义的那个数组元素因此可以在其中定义子表甚至嵌套的数组的表[[fruits]] name apple [fruits.physical] # subtable color red shape round [[fruits.varieties]] # nested array of tables name red delicious [[fruits.varieties]] name granny smith [[fruits]] name banana [[fruits.varieties]] name plantain这里有三种必须在解析期报错的非法情况值得特别注意子结构先于其父数组元素定义如先写[fruit.physical]后写[[fruit]]向静态定义的数组追加元素fruits []之后再写[[fruits]]同一名称在普通表与数组的表之间来回重定义[[fruits.varieties]]之后又写[fruits.varieties]。此外内联表可以出现在数组内部构成对象数组的紧凑写法points [ { x 1, y 2, z 3 }, { x 7, y 8, z 9 }, { x 2, y 4, z 8 } ]形式化文法ABNF规范文档 的最后一节明确指出TOML 的形式化语法以独立的 ABNF 文件 呈现。该文件基于 RFC 5234 的 ABNF 格式书写是全仓库唯一被标记为canonical的语法定义见 CHANGELOG.md 中 1.0.0-rc.3 的说明也是实现与测试器开发者的权威参考。在 toml.abnf 中可以看到规范文本逐字对应的文法骨架toml expression *( newline expression ) expression ws [ comment ] expression / ws keyval ws [ comment ] expression / ws table ws [ comment ]例如裸键的文法约束unquoted-key 1*( ALPHA / DIGIT / %x2D / %x5F )正是规范中裸键只能包含A-Za-z0-9_-的形式化表达整数与浮点分别由dec-int / hex-int / oct-int / bin-int与float-int-part ( exp / frac [ exp ] )定义日期时间则完整继承了 RFC 3339 的结构offset-date-time full-date time-delim full-time local-date-time full-date time-delim partial-time local-date full-date local-time partial-timeABNF 文件头部还附带了实用提示可以通过 instaparse 之类的工具在浏览器里交互式验证文法与文档的匹配关系将输入格式切换为 ABNF 后粘贴整个文法即可试解析。所有合法 TOML 文档都能匹配该文法同时规范文本补充了文法无法表达、需要语义层拒绝的规则如重复定义键。版本演进与规范发布流程本仓库同时维护了规范正文toml.md与 CHANGELOG.md从中可以清晰地看到 TOML 的能力演化脉络1.1.02025-12-18内联表允许换行与末尾逗号基础字符串新增\xHH转义新增\e转义日期时间与时间的秒数变为可选1.0.02021-01-11首个稳定版本明确点号键建表语义、顶层表描述、缩进忽略等规则0.5.02018-07-11加入点号键、十六/八/二进制整数、inf/nan特殊浮点、Local Date-Time / Local Date / Local Time、ABNF 规范、.toml扩展名与application/tomlMIME 类型等0.4.02015-02-12加入内联表、数字下划线移除正斜杠转义0.3.02014-11-10加入科学计数法、可选前缀、RFC 3339 日期时间、多行与字面字符串0.2.02013-09-24引入表术语与可嵌套的表数组0.1.02013-03-17首个正式版本版本号遵循 SemVer。规范的发版路径被自动化在 scripts/release.py 中它在规范仓库与 toml.io 网站仓库之间编排更新 CHANGELOG → 打标签 → 将 toml.md 拷贝为specs/en/v{version}.md→ 更新 ABNF 链接与网站重定向 → 推送的全流程并在发布前校验两个仓库的 upstream 指向、分支与工作区状态详见 docs/README.md 的 Maintainer documentation。这也印证了仓库的自我定位它是规范的在途in-development版本已发布版本在 toml.io 网站归档。结语何时选择 TOML回到 README.md 的定位TOML 是容易读、语义明显、无歧义映射哈希表、易于多语言解析的最小配置格式。当你需要的是人类可维护、带注释、支持日期与嵌套结构、且有严格规范背书的配置文件时TOML 是比 JSON无注释、YAML复杂语义和 INI无标准、嵌套能力弱更顺手的中间选项而当你的需求超出配置范畴——例如序列化任意数据结构——则应回到通用序列化格式。本文覆盖的全部语法细节均可在本仓库的 toml.md规范正文与 toml.abnf形式文法中逐条核对动手实现或评测解析器时请以这两份文档为准。【免费下载链接】tomlToms Obvious, Minimal Language项目地址: https://gitcode.com/gh_mirrors/to/toml创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进