├── CHANGELOG.md ├── LICENSE ├── README.md ├── logos └── toml-200.png ├── toml-v0.5.0.md ├── toml-v1.0.0.md ├── toml.abnf └── toml.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # TOML 变更记录 2 | 3 | ## 未发行 4 | 5 | * 明确点分隔键是在哪里以及如何定义表的。 6 | 7 | ## 1.0.0 / 2021-01-11 8 | 9 | * 明确表是如何被创建和定义的。 10 | * 明确阐释顶层表。 11 | * 明确键名前的缩进会被忽略。 12 | * 明确表头前的缩进会被忽略。 13 | * 明确数组值之间的缩进会被忽略。 14 | 15 | ## 1.0.0-rc.3 / 2020-10-07 16 | 17 | * 明确数组中的逗号前允许注释和换行。 18 | * 以 ABNF 为准,并在文本规范中对其进行引用。 19 | 20 | ## 1.0.0-rc.2 / 2020-08-09 21 | 22 | * 创建 https://toml.io 作为阅读 TOML 规范的首选地址。 23 | * 明确“引号”的含义。 24 | * 明确“期望”值范围的含义。 25 | * 明确键/值对后允许结束文件。 26 | * 明确键名的各种书写风格是等价的。 27 | * 明确多行字符串中的行末反斜杠必须是未转义的。 28 | * 增加错误的浮点数写法示例。 29 | 30 | ## 1.0.0-rc.1 / 2020-04-01 31 | 32 | * 在 ABNF 中明确,要怎样在多行基础与多行字面量字符串中使用引号。 33 | * 允许浮点数的指数部分有前导零。 34 | * 明确控制字符不允许出现在注释里。 35 | * 明确点分隔键隐式定义表的行为。 36 | * 明确行内表是不可变动的。 37 | * 明确行内表中不允许尾逗号。 38 | * 在 ABNF 中明确,字符串或注释中不允许出现 UTF-16 代理码点(U+D800—U+DFFF)。 39 | * 允许基础字符串和多行基础字符串中直接使用 Tab 字符。 40 | * 允许数组中的值类型不同。 41 | 42 | ## 0.5.0 / 2018-07-11 43 | 44 | * 添加点分隔键。 45 | * 添加十六进制、八进制和二进制整数格式。 46 | * 添加特殊浮点值(inf,nan)。 47 | * 将日期时间重命名为坐标日期时间。 48 | * 添加各地日期时间。 49 | * 添加各地日期。 50 | * 添加各地时间。 51 | * 添加 ABNF 规范。 52 | * 允许用空格(而不是 T)在日期时间中分隔日期和时间。 53 | * 允许多行基本字符串中,行延续运算符的反斜杠和换行之间,意外的空白。 54 | * 指定标准文件扩展名为 `.toml`。 55 | * 指定 MIME 类型为 `application/toml`。 56 | * 明确 U+007F 为需转义字符。 57 | * 明确键名始终是字符串。 58 | * 明确表数组不能用于向静态数组追加内容。 59 | * 明确 TOML 文件必须是合法的 UTF-8 文档。 60 | * 明确数组内值的合法性。 61 | * 明确字面量字符串可以作为表的键名。 62 | * 明确日期时间和时间至少应当具有毫秒级的精度。 63 | * 明确多行数组中可以有注释。 64 | * 明确 +0、-0、+0.0 和 -0.0 的合法性以及它们的含义。 65 | * TOML 有 logo 了! 66 | 67 | ## 0.4.0 / 2015-02-12 68 | 69 | * 增加行内表语法。 70 | * 允许数字里的下划线。 71 | * 将正斜杠从可转义字符中移除。 72 | * Unicode 转义必须是标量值。 73 | * 换行现在被定义为 LF 或 CRLF。 74 | 75 | ## 0.3.1 / 2014-11-11 76 | 77 | * 修正错误的日期时间示例。 78 | 79 | ## 0.3.0 / 2014-11-10 80 | 81 | * 增加浮点数的科学记数表示法。 82 | * 整数允许可选的加号前缀。 83 | * 日期时间改为 RFC 3339 格式(允许坐标和小数秒)。 84 | * 增加多行和字面量字符串。 85 | * 明确合法键名可以包含哪些字符。 86 | 87 | ## 0.2.0 / 2013-09-24 88 | 89 | * 使用“表”来取代术语“键群”。 90 | * 增加可以嵌套的表数组的定义功能。 91 | 92 | ## 0.1.0 / 2013-03-17 93 | 94 | * 从怒刷推特屏(原文:[Twitter rage])到现实;TOML 现在成形问世了。 95 | * 第一次较好的发布。 96 | * TOML 版本号采取 SemVer 标准。 97 | 98 | [Twitter rage]: https://www.urbandictionary.com/define.php?term=Twitter%20Rage "Urban Dictionary: At least 5 consecutive angry tweets about something. Usually results in a drop in followers and/or more people twitter raging. 都市词典:至少 5 条连续的关于某事愤怒的推文。通常会导致他人取关和/或更多的人被传染。" 99 | 100 | ——[龙腾道](https://github.com/LongTengDao) 译 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) Tom Preston-Werner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TOML logo 2 | 3 | TOML 4 | ==== 5 | 6 | 汤小明的小巧明晰语言。 7 | 8 | 作者:[汤姆·普雷斯顿—维尔纳](https://github.com/mojombo)、[Pradyun Gedam](https://github.com/pradyunsg) 等人。 9 | 10 | > `toml.md` 是研发中的版本。`toml-v1.0.0.md` 等文件名中含版本号的为发行版。 11 | > (原文:该存储库包含了 TOML 规范的研发版本。你可以在 [https://toml.io](https://toml.io/cn/) 查看发行版。) 12 | 13 | 宗旨 14 | ---- 15 | 16 | TOML 旨在成为一个语义显著而易于阅读的最低限度的配置文件格式。 17 | TOML 被设计地能够无歧义地转化为哈希表。 18 | TOML 应当能简单地解析成形形色色的语言中的数据结构。 19 | 20 | 示例 21 | ---- 22 | 23 | ```toml 24 | # 这是一个 TOML 文档。 25 | 26 | title = "TOML 示例" 27 | 28 | [owner] 29 | name = "汤姆·普雷斯顿—维尔纳" 30 | dob = 1979-05-27T07:32:00-08:00 # 位列一等公民的日期,可以直接书写 31 | 32 | [database] 33 | server = "192.168.1.1" 34 | ports = [ 8000, 8001, 8002 ] 35 | connection_max = 5000 36 | enabled = true 37 | 38 | [servers] 39 | 40 | # 允许缩进(Tab 和/或空格),不过不是必要的 41 | [servers.alpha] 42 | ip = "10.0.0.1" 43 | dc = "eqdc10" 44 | 45 | [servers.beta] 46 | ip = "10.0.0.2" 47 | dc = "eqdc10" 48 | 49 | [clients] 50 | data = [ ["gamma", "delta"], [1, 2] ] 51 | 52 | # 数组中是可以换行的 53 | hosts = [ 54 | "alpha", 55 | "omega" 56 | ] 57 | ``` 58 | 59 | 与其它格式的比较 60 | ---------------- 61 | 62 | TOML 享有其它用于应用配置和数据序列化的文件格式,诸如 YAML 和 JSON 的共同特性。 63 | TOML 和 JSON 都是简单的,且使用普适的数据类型,这使得它们容易为机器编写或解析。 64 | TOML 和 YAML 都强调人类可读的特性,像是注释,这使得搞懂给定行的用意更容易。 65 | TOML 的不同之处在于结合了这些特性,允许注释(不像 JSON)但保留简单性(不像 YAML)。 66 | 67 | 因为 TOML 是明确作为配置文件格式设计的,解析它是简单的,但它不是为序列化任意数据结构设计的。 68 | TOML 总是以哈希表作为文件的顶层,这便于包含键内数据嵌套,但它不允许顶层数组或浮点数,所以它不能直接序列化某些数据。 69 | 也不存在标准的 TOML 文件开始或结束标识,用于通过复合流发送。 70 | 这些细节必须在应用层协商。 71 | 72 | INI 文件也常被用来同 TOML 比较,因为它们在语法和作为配置文件使用上的相似性。 73 | 然而……INI 并无标准格式,并且不能优雅地料理超过一或两层的嵌套。 74 | 75 | 延伸阅读: 76 | 77 | * YAML 规范:https://yaml.org/spec/1.2/spec.html 78 | * JSON 规范:https://tools.ietf.org/html/rfc8259 79 | * Wikipedia 上的 INI 文件: https://en.wikipedia.org/wiki/INI_file 80 | 81 | 参与 82 | ---- 83 | 84 | 欢迎帮助文档、问题反馈、修缮合并,以及其它一切形式的贡献! 85 | 86 | 百科 87 | ---- 88 | 89 | 我们有一个[官方 TOML 百科](https://github.com/toml-lang/toml/wiki)收录了以下内容: 90 | 91 | * 使用了 TOML 的项目 92 | * 可用实现清单 93 | * 验证程序 94 | * TOML 解码器和编码器的语言无关测试套件 95 | * 编辑器支持 96 | * 编码器 97 | * 格式转换器 98 | 99 | 请点进去看一下,如果你想要阅览或加入其中。 100 | 感谢你成为 TOML 社区的一员! 101 | 102 | ——[龙腾道](https://github.com/LongTengDao) 译 -------------------------------------------------------------------------------- /logos/toml-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LongTengDao/TOML/be58d64b744189ad1466b749c04f4f6f0c7c48f8/logos/toml-200.png -------------------------------------------------------------------------------- /toml-v0.5.0.md: -------------------------------------------------------------------------------- 1 | ![TOML Logo](logos/toml-200.png) 2 | 3 | TOML v0.5.0 4 | =========== 5 | 6 | 汤小明的小巧明晰语言。 7 | 8 | [汤姆·普雷斯顿—维尔纳](https://github.com/mojombo)所创,[龙腾道](https://github.com/LongTengDao)译。 9 | 10 | 自版本 0.5.0 起,TOML 可以说是非常稳定了。 11 | 我们的目标是将来正式的 1.0.0 版能够向下兼容到 0.5.0 版(尽人事之可能吧)。 12 | 强烈建议所有的实现都着手兼容 0.5.0,以便于到时转化成 1.0.0 能简单一些。 13 | 14 | 宗旨 15 | ---- 16 | 17 | TOML 旨在成为一个语义显著而易于阅读的最低限度的配置文件格式。 18 | TOML 被设计地能够无歧义地转化为哈希表。 19 | TOML 应当能简单地解析成形形色色的语言中的数据结构。 20 | 21 | 目录 22 | ---- 23 | 24 | - [示例](#user-content-example) 25 | - [说明](#user-content-spec) 26 | - [注释](#user-content-comment) 27 | - [键值对](#user-content-keyvalue-pair) 28 | - [键名](#user-content-keys) 29 | - [字符串](#user-content-string) 30 | - [整数](#user-content-integer) 31 | - [浮点数](#user-content-float) 32 | - [布尔值](#user-content-boolean) 33 | - [坐标日期时刻](#user-content-offset-date-time) 34 | - [各地日期时刻](#user-content-local-date-time) 35 | - [各地日期](#user-content-local-date) 36 | - [各地时刻](#user-content-local-time) 37 | - [数组](#user-content-array) 38 | - [表](#user-content-table) 39 | - [行内表](#user-content-inline-table) 40 | - [表数组](#user-content-array-of-tables) 41 | - [文件扩展名](#user-content-filename-extension) 42 | - [MIME 类型](#user-content-mime-type) 43 | - [与其它格式的比较](#user-content-comparison-with-other-formats) 44 | - [参与](#user-content-get-involved) 45 | - [百科](#user-content-wiki) 46 | 47 | [示例](#user-content-example)  48 | ------ 49 | 50 | ```toml 51 | # 这是一个 TOML 文档。 52 | 53 | title = "TOML 示例" 54 | 55 | [owner] 56 | name = "汤姆·普雷斯顿—维尔纳" 57 | dob = 1979-05-27T07:32:00-08:00 # 第一类日期时刻 58 | 59 | [database] 60 | server = "192.168.1.1" 61 | ports = [ 8001, 8001, 8002 ] 62 | connection_max = 5000 63 | enabled = true 64 | 65 | [servers] 66 | 67 | # 允许缩进(Tab 和/或空格),不过不是必要的 68 | [servers.alpha] 69 | ip = "10.0.0.1" 70 | dc = "eqdc10" 71 | 72 | [servers.beta] 73 | ip = "10.0.0.2" 74 | dc = "eqdc10" 75 | 76 | [clients] 77 | data = [ ["gamma", "delta"], [1, 2] ] 78 | 79 | # 数组中是可以换行的 80 | hosts = [ 81 | "alpha", 82 | "omega" 83 | ] 84 | ``` 85 | 86 | [说明](#user-content-spec)  87 | ------ 88 | 89 | * TOML 是大小写敏感的。 90 | * TOML 文件必须是有效的 UTF-8 编码的 Unicode 文档。 91 | * 空白的意思是 Tab(0x09)或空格(0x20)。 92 | * 换行的意思是 LF(0x0A)或 CRLF(0x0D0A)。 93 | 94 | [注释](#user-content-comment)  95 | ------ 96 | 97 | 井号将此行剩下的部分标记为注释。 98 | 99 | ```toml 100 | # 这是一个全行注释 101 | key = "value" # 这是一个行末注释 102 | ``` 103 | 104 | [键值对](#user-content-keyvalue-pair)  105 | -------- 106 | 107 | TOML 文档最基本的构成区块是键值对。 108 | 109 | 键名在等号的左边而值在右边。 110 | 键名和键值周围的空白会被忽略。 111 | 键、等号和值必须在同一行(不过有些值可以跨多行)。 112 | 113 | ```toml 114 | key = "value" 115 | ``` 116 | 117 | 值必须是这些类型:字符串,整数,浮点数,布尔值,日期时刻,数组,或行内表。 118 | 不指定值是有误的。 119 | 120 | ```toml 121 | key = # 有误 122 | ``` 123 | 124 | [键名](#user-content-keys)  125 | ------ 126 | 127 | 键名可以是裸露的,引号引起来的,或点分隔的。 128 | 129 | **裸键**只能包含 ASCII 字母,ASCII 数字,下划线和短横线(`A-Za-z0-9_-`)。 130 | 注意裸键允许仅由纯 ASCII 数字构成,例如 `1234`,但是是被理解为字符串的。 131 | 132 | ```toml 133 | key = "value" 134 | bare_key = "value" 135 | bare-key = "value" 136 | 1234 = "value" 137 | ``` 138 | 139 | **引号键**遵循与基础字符串或字面量字符串相同的规则并允许你使用更为广泛的键名。 140 | 除非明显必要,使用裸键方为最佳实践。 141 | 142 | ```toml 143 | "127.0.0.1" = "value" 144 | "character encoding" = "value" 145 | "ʎǝʞ" = "value" 146 | 'key2' = "value" 147 | 'quoted "value"' = "value" 148 | ``` 149 | 150 | 裸键不能为空,但空引号键是允许的(虽然不建议如此)。 151 | 152 | ```toml 153 | = "no key name" # 有误 154 | "" = "blank" # 有效但不建议 155 | '' = 'blank' # 有效但不建议 156 | ``` 157 | 158 | **点分隔键**是一系列通过点相连的裸键或引号键。 159 | 这允许了你将相近属性放在一起: 160 | 161 | ```toml 162 | "名称" = "橙子" 163 | "物理属性"."颜色" = "橙色" 164 | "物理属性"."形状" = "圆形" 165 | site."google.com" = true 166 | ``` 167 | 168 | 这在 JSON 那儿,是以下结构: 169 | 170 | ```json 171 | { 172 | "名称": "橙子", 173 | "物理属性": { 174 | "颜色": "橙色", 175 | "形状": "圆形" 176 | }, 177 | "site": { 178 | "google.com": true 179 | } 180 | } 181 | ``` 182 | 183 | 点分隔符周围的空白会被忽略,不过,最佳实践是不要使用任何不必要的空白。 184 | 185 | 多次定义同一个键是不行的。 186 | 187 | ``` 188 | # 不要这样做 189 | name = "汤姆" 190 | name = "Pradyun" 191 | ``` 192 | 193 | 只要一个键还没有被直接定义过,你就仍可以对它和它下属的键名赋值。 194 | 195 | ``` 196 | a.b.c = 1 197 | a.d = 2 198 | ``` 199 | 200 | ``` 201 | # 这就不行了 202 | a.b = 1 203 | a.b.c = 2 204 | ``` 205 | 206 | [字符串](#user-content-string)  207 | -------- 208 | 209 | 共有四种方式来表示字符串:基础式的,多行基础式的,字面量式的,和多行字面量式的。 210 | 所有字符串都只能包含有效的 UTF-8 字符。 211 | 212 | **基础字符串**由引号包裹。 213 | 任何 Unicode 字符都可以使用,除了那些必须转义的:引号,反斜杠,以及控制字符(U+0000 至 U+001F,U+007F)。 214 | 215 | ```toml 216 | str = "我是一个字符串。\"你可以把我引起来\"。姓名\tJos\u00E9\n位置\t旧金山。" 217 | ``` 218 | 219 | 为了方便,一些流行的字符有其简便转义写法。 220 | 221 | ``` 222 | \b - backspace (U+0008) 223 | \t - tab (U+0009) 224 | \n - linefeed (U+000A) 225 | \f - form feed (U+000C) 226 | \r - carriage return (U+000D) 227 | \" - quote (U+0022) 228 | \\ - backslash (U+005C) 229 | \uXXXX - unicode (U+XXXX) 230 | \UXXXXXXXX - unicode (U+XXXXXXXX) 231 | ``` 232 | 233 | 任何 Unicode 字符都可以用 `\uXXXX` 或 `\UXXXXXXXX` 的形式来转义。 234 | 转义码必须是有效的 Unicode [标量值](https://unicode.org/glossary/#unicode_scalar_value)。 235 | 236 | 所有上面未列出的其它转义序列都是保留的,如果被用了,TOML 应当生成一个错误。 237 | 238 | 有时你需要表示一小篇文本(例如译文)或者想要对非常长的字符串进行折行。 239 | TOML 对此进行了简化。 240 | 241 | **多行基础字符串**由三个引号包裹,允许折行。 242 | 紧随开头引号的那个换行会被去除。 243 | 其它空白和换行会被原样保留。 244 | 245 | ```toml 246 | str1 = """ 247 | 玫瑰是红色的 248 | 紫罗兰是蓝色的""" 249 | ``` 250 | 251 | TOML 解析器可以相对灵活地解析成对所在平台有效的换行字符。 252 | 253 | ```toml 254 | # 在 Unix 系统,上面的多行字符串可能等同于: 255 | str2 = "玫瑰是红色的\n紫罗兰是蓝色的" 256 | 257 | # 在 Windows 系统,它可能等价于: 258 | str3 = "玫瑰是红色的\r\n紫罗兰是蓝色的" 259 | ``` 260 | 261 | 想书写长字符串却不想引入无关空白,可以用“行末反斜杠”。 262 | 当一行的最后一个非空白字符是 `\` 时,它会连同它后面的所有空白(包括换行)一起被去除,直到下一个非空白字符或结束引号为止。 263 | 所有对基础字符串有效的转义序列,对多行基础字符串也同样适用。 264 | 265 | ```toml 266 | # 下列字符串的每一个字节都完全相同: 267 | str1 = "那只敏捷的棕狐狸跳过了那只懒狗。" 268 | 269 | str2 = """ 270 | 那只敏捷的棕\ 271 | 272 | 273 | 狐狸跳过了\ 274 | 那只懒狗。""" 275 | 276 | str3 = """\ 277 | 那只敏捷的棕\ 278 | 狐狸跳过了\ 279 | 那只懒狗。\ 280 | """ 281 | ``` 282 | 283 | 任何 Unicode 字符都可以使用,除了那些必须被转义的:反斜杠和控制字符(U+0000 至 U+001F,U+007F)。 284 | 引号不需要转义,除非它们的存在会造成一个比预期提前的结束标记。 285 | 286 | 如果你常常要指定 Windows 路径或正则表达式,那么必须转义反斜杠就马上成为啰嗦而易错的了。 287 | 为了帮助搞定这点,TOML 支持字面量字符串,它完全不允许转义。 288 | 289 | **字面量字符串**由单引号包裹。 290 | 类似于基础字符串,他们只能表现为单行: 291 | 292 | ```toml 293 | # 所见即所得。 294 | winpath = 'C:\Users\nodejs\templates' 295 | winpath2 = '\\ServerX\admin$\system32\' 296 | quoted = '汤姆·"达布斯"·普雷斯顿—维尔纳' 297 | regex = '<\i\c*\s*>' 298 | ``` 299 | 300 | 由于没有转义,无法在由单引号包裹的字面量字符串中写入单引号。 301 | 万幸,TOML 支持一种多行版本的字面量字符串来解决这个问题。 302 | 303 | **多行字面量字符串**两侧各有三个单引号来包裹,允许换行。 304 | 类似于字面量字符串,无论任何转义都不存在。 305 | 紧随开始标记的那个换行会被剔除。 306 | 开始结束标记之间的所有其它内容会原样对待。 307 | 308 | ```toml 309 | regex2 = '''I [dw]on't need \d{2} apples''' 310 | lines = ''' 311 | 原始字符串中的 312 | 第一个换行被剔除了。 313 | 所有其它空白 314 | 都保留了。 315 | ''' 316 | ``` 317 | 318 | 除 Tab 以外的所有控制字符都不允许出现在字面量字符串中。 319 | 因此,对于二进制数据,建议你使用 Base64 或其它合适的 ASCII 或 UTF-8 编码。 320 | 对那些编码的处理方式,将交由应用程序自己来确定。 321 | 322 | [整数](#user-content-integer)  323 | ------ 324 | 325 | 整数是纯数字。 326 | 正数可以有加号前缀。 327 | 负数的前缀是减号。 328 | 329 | ```toml 330 | int1 = +99 331 | int2 = 42 332 | int3 = 0 333 | int4 = -17 334 | ``` 335 | 336 | 对于大数,你可以在数字之间用下划线来增强可读性。 337 | 每个下划线两侧必须至少有一个数字。 338 | 339 | ```toml 340 | int5 = 1_000 341 | int6 = 5_349_221 342 | int7 = 1_2_3_4_5 # 无误但不鼓励 343 | ``` 344 | 345 | 前导零是不允许的。 346 | 整数值 `-0` 与 `+0` 是有效的,并等同于无前缀的零。 347 | 348 | 非负整数值也可以用十六进制、八进制或二进制来表示。 349 | 在这些格式中,(前缀后的)前导零是允许的。 350 | 十六进制值大小写不敏感。 351 | 数字间的下划线是允许的(但不能存在于前缀和值之间)。 352 | 353 | ```toml 354 | # 带有 `0x` 前缀的十六进制 355 | hex1 = 0xDEADBEEF 356 | hex2 = 0xdeadbeef 357 | hex3 = 0xdead_beef 358 | 359 | # 带有 `0o` 前缀的八进制 360 | oct1 = 0o01234567 361 | oct2 = 0o755 # 对于表示 Unix 文件权限很有用 362 | 363 | # 带有 `0b` 前缀的二进制 364 | bin1 = 0b11010110 365 | ``` 366 | 367 | 取值范围要求为 64 比特(signed long)(−9,223,372,036,854,775,808 至 9,223,372,036,854,775,807)。 368 | 369 | [浮点数](#user-content-float)  370 | -------- 371 | 372 | 浮点数应当被实现为 IEEE 754 binary64 值。 373 | 374 | 一个浮点数由一个整数部分(遵从与整数值相同的规则)后跟上一个小数部分和/或一个指数部分组成。 375 | 如果小数部分和指数部分兼有,那小数部分必须在指数部分前面。 376 | 377 | ```toml 378 | # 小数 379 | flt1 = +1.0 380 | flt2 = 3.1415 381 | flt3 = -0.01 382 | 383 | # 指数 384 | flt4 = 5e+22 385 | flt5 = 1e6 386 | flt6 = -2E-2 387 | 388 | # 都有 389 | flt7 = 6.626e-34 390 | ``` 391 | 392 | 小数部分是一个小数点后跟一个或多个数字。 393 | 394 | 一个指数部分是一个 E(大小写均可)后跟一个整数部分(遵从与整数值相同的规则)。 395 | 396 | 与整数相似,你可以使用下划线来增强可读性。 397 | 每个下划线必须被至少一个数字围绕。 398 | 399 | ```toml 400 | flt8 = 9_224_617.445_991_228_313 401 | ``` 402 | 403 | 浮点数值 `-0.0` 与 `+0.0` 是有效的,并且应当遵从 IEEE 754。 404 | 405 | 特殊浮点值也能够表示。 406 | 它们是小写的。 407 | 408 | ```toml 409 | # 无穷 410 | sf1 = inf # 正无穷 411 | sf2 = +inf # 正无穷 412 | sf3 = -inf # 负无穷 413 | 414 | # 非数 415 | sf4 = nan # 实际上对应信号非数码还是静默非数码,取决于实现 416 | sf5 = +nan # 等同于 `nan` 417 | sf6 = -nan # 有效,实际码取决于实现 418 | ``` 419 | 420 | [布尔值](#user-content-boolean)  421 | -------- 422 | 423 | 布尔值就是你所惯用的那样。 424 | 要小写。 425 | 426 | ```toml 427 | bool1 = true 428 | bool2 = false 429 | ``` 430 | 431 | [坐标日期时刻](#user-content-offset-date-time)  432 | -------------- 433 | 434 | 要明确无误地表示世上的一个特定时间,你可以使用指定了时区偏移量的 [RFC 3339](https://tools.ietf.org/html/rfc3339) 格式的日期时刻。 435 | 436 | ```toml 437 | odt1 = 1979-05-27T07:32:00Z 438 | odt2 = 1979-05-27T00:32:00-07:00 439 | odt3 = 1979-05-27T00:32:00.999999-07:00 440 | ``` 441 | 442 | 出于可读性的目的,你可以用空格替代日期和时刻中间的 T(RFC 3339 的第 5.6 节中允许了这样做)。 443 | 444 | ```toml 445 | odt4 = 1979-05-27 07:32:00Z 446 | ``` 447 | 448 | 小数秒的精度取决于实现,但至少应当能够精确到毫秒。 449 | 如果它的值超出了实现所支持的精度,那多余的部分必须被舍弃,而不能四舍五入。 450 | 451 | [各地日期时刻](#user-content-local-date-time)  452 | -------------- 453 | 454 | 如果你省略了 [RFC 3339](https://tools.ietf.org/html/rfc3339) 日期时刻中的时区偏移量,这表示该日期时刻的使用并不涉及时区偏移。 455 | 在没有其它信息的情况下,并不知道它究竟该被转化成世上的哪一刻。 456 | 如果仍被要求转化,那结果将取决于实现。 457 | 458 | ```toml 459 | ldt1 = 1979-05-27T07:32:00 460 | ldt2 = 1979-05-27T00:32:00.999999 461 | ``` 462 | 463 | 小数秒的精度取决于实现,但至少应当能够精确到毫秒。 464 | 如果它的值超出了实现所支持的精度,那多余的部分必须被舍弃,而不能四舍五入。 465 | 466 | [各地日期](#user-content-local-date)  467 | ---------- 468 | 469 | 如果你只写了 [RFC 3339](https://tools.ietf.org/html/rfc3339) 日期时刻中的日期部分,那它表示一整天,同时也不涉及时区偏移。 470 | 471 | ```toml 472 | ld1 = 1979-05-27 473 | ``` 474 | 475 | [各地时刻](#user-content-local-time)  476 | ---------- 477 | 478 | 如果你只写了 [RFC 3339](https://tools.ietf.org/html/rfc3339) 日期时刻中的时刻部分,它将只表示一天之中的那个时刻,而与任何特定的日期无关、亦不涉及时区偏移。 479 | 480 | ```toml 481 | lt1 = 07:32:00 482 | lt2 = 00:32:00.999999 483 | ``` 484 | 485 | 小数秒的精度取决于实现,但至少应当能够精确到毫秒。 486 | 如果它的值超出了实现所支持的精度,那多余的部分必须被舍弃,而不能四舍五入。 487 | 488 | [数组](#user-content-array)  489 | ------ 490 | 491 | 数组是内含值的方括号。 492 | 空白会被忽略。 493 | 子元素由逗号分隔。 494 | 子元素的数据类型必须一致(不同写法的字符串应当被认为是相同的类型,不同元素类型的数组也同是数组类型)。 495 | 496 | ```toml 497 | arr1 = [ 1, 2, 3 ] 498 | arr2 = [ "红", "黄", "绿" ] 499 | arr3 = [ [ 1, 2 ], [3, 4, 5] ] 500 | arr4 = [ "所有的", '字符串', """是相同的""", '''类型''' ] 501 | arr5 = [ [ 1, 2 ], ["a", "b", "c"] ] 502 | 503 | arr6 = [ 1, 2.0 ] # 有误 504 | ``` 505 | 506 | 数组也可以跨多行。 507 | 数组的最后一个值后面可以有终逗号(也称为尾逗号)。 508 | 值和结束括号前可以存在任意数量的换行和注释。 509 | 510 | ```toml 511 | arr7 = [ 512 | 1, 2, 3 513 | ] 514 | 515 | arr8 = [ 516 | 1, 517 | 2, # 这是可以的 518 | ] 519 | ``` 520 | 521 | [表](#user-content-table)  522 | ---- 523 | 524 | 表(也被称为哈希表或字典)是键值对的集合。 525 | 它们在方括号里,并作为单独的行出现。 526 | 看得出它们不同于数组,因为数组只有值。 527 | 528 | ```toml 529 | [table] 530 | ``` 531 | 532 | 在它下方,直至下一个表或文件结束,都是这个表的键值对。 533 | 表不保证保持键值对的指定顺序。 534 | 535 | ```toml 536 | [table-1] 537 | key1 = "some string" 538 | key2 = 123 539 | 540 | [table-2] 541 | key1 = "another string" 542 | key2 = 456 543 | ``` 544 | 545 | 表名的规则与键名相同(见前文键名定义)。 546 | 547 | ```toml 548 | [dog."tater.man"] 549 | type.name = "pug" 550 | ``` 551 | 552 | 这在 JSON 那儿,是以下结构: 553 | 554 | ```json 555 | { "dog": { "tater.man": { "type": { "name": "pug" } } } } 556 | ``` 557 | 558 | 键名周围的空格会被忽略,然而最佳实践还是不要有任何多余的空白。 559 | 560 | ```toml 561 | [a.b.c] # 这是最佳实践 562 | [ d.e.f ] # 等同于 [d.e.f] 563 | [ g . h . i ] # 等同于 [g.h.i] 564 | [ j . "ʞ" . 'l' ] # 等同于 [j."ʞ".'l'] 565 | ``` 566 | 567 | 你不必层层完整地写出你不想写的所有途径的父表。 568 | TOML 知道该怎么办。 569 | 570 | ```toml 571 | # [x] 你 572 | # [x.y] 不 573 | # [x.y.z] 需要这些 574 | [x.y.z.w] # 来让这生效 575 | ``` 576 | 577 | 空表是允许的,只要里面没有键值对就行了。 578 | 579 | 类似于键名,你不能重复定义任何表。 580 | 这样做是错误的。 581 | 582 | ``` 583 | # 不要这样做 584 | 585 | [a] 586 | b = 1 587 | 588 | [a] 589 | c = 2 590 | ``` 591 | 592 | ``` 593 | # 也不要这样做 594 | 595 | [a] 596 | b = 1 597 | 598 | [a.b] 599 | c = 2 600 | ``` 601 | 602 | [行内表](#user-content-inline-table)  603 | -------- 604 | 605 | 行内表提供了一种更为紧凑的语法来表示表。 606 | 对于否则就很啰嗦的成组数据,这尤其有用。 607 | 行内表由花括号 `{` 和 `}` 包裹。 608 | 在括号中,可以出现零或更多个以逗号分隔的键值对。 609 | 键值对采取与标准表中的键值对相同的形式。 610 | 什么类型的值都可以,包括行内表。 611 | 612 | 行内表得出现在同一行内。 613 | 不允许花括号中出现任何换行,除非在值中它们有效。 614 | 即便如此,也强烈不建议把一个行内表搞成纵跨多行的样子。 615 | 如果你发现自己真的需要,那意味着你应该使用标准表。 616 | 617 | ```toml 618 | name = { first = "汤姆", last = "普雷斯顿—维尔纳" } 619 | point = { x = 1, y = 2 } 620 | animal = { type.name = "哈巴狗" } 621 | ``` 622 | 623 | 上述行内表等同于下面的标准表定义: 624 | 625 | ```toml 626 | [name] 627 | first = "汤姆" 628 | last = "普雷斯顿—维尔纳" 629 | 630 | [point] 631 | x = 1 632 | y = 2 633 | 634 | [animal] 635 | type.name = "哈巴狗" 636 | 637 | ``` 638 | 639 | [表数组](#user-content-array-of-tables)  640 | -------- 641 | 642 | 最后还剩下一个没法表示的是表数组。 643 | 这可以通过把表名写在双方括号里来表示。 644 | 各个具有相同方括号名的表将会成为该数组内的一员。 645 | 这些表遵循它们出现的顺序。 646 | 一个没有任何键值对的双方括号表将被视为一个空表。 647 | 648 | ```toml 649 | [[products]] 650 | name = "锤子" 651 | sku = 738594937 652 | 653 | [[products]] 654 | 655 | [[products]] 656 | name = "钉子" 657 | sku = 284758393 658 | color = "灰色" 659 | ``` 660 | 661 | 这在 JSON 那儿,是以下结构。 662 | 663 | ```json 664 | { 665 | "products": [ 666 | { "name": "锤子", "sku": 738594937 }, 667 | { }, 668 | { "name": "钉子", "sku": 284758393, "color": "灰色" } 669 | ] 670 | } 671 | ``` 672 | 673 | 你还可以创建一个嵌套表数组。 674 | 只要在子表上使用相同的双方括号语法语法。 675 | 每个双方括号子表将隶属于上方最近定义的表元素。 676 | 677 | ```toml 678 | [[fruit]] 679 | name = "苹果" 680 | 681 | [fruit.physical] 682 | color = "红色" 683 | shape = "圆形" 684 | 685 | [[fruit.variety]] 686 | name = "蛇果" 687 | 688 | [[fruit.variety]] 689 | name = "澳洲青苹" 690 | 691 | [[fruit]] 692 | name = "香蕉" 693 | 694 | [[fruit.variety]] 695 | name = "车前草" 696 | ``` 697 | 698 | 上述 TOML 对应下面的 JSON。 699 | 700 | ```json 701 | { 702 | "fruit": [ 703 | { 704 | "name": "苹果", 705 | "physical": { 706 | "color": "红色", 707 | "shape": "圆形" 708 | }, 709 | "variety": [ 710 | { "name": "蛇果" }, 711 | { "name": "澳洲青苹" } 712 | ] 713 | }, 714 | { 715 | "name": "香蕉", 716 | "variety": [ 717 | { "name": "车前草" } 718 | ] 719 | } 720 | ] 721 | } 722 | ``` 723 | 724 | 若试图向一个静态定义的数组追加内容,即便数组尚且为空或类型兼容,也必须在解析时报错。 725 | 726 | ```toml 727 | # 无效的 TOML 文档 728 | fruit = [] 729 | 730 | [[fruit]] # 不允许 731 | ``` 732 | 733 | 若试图用已经确定为数组的名称定义表,必须在解析时报错。 734 | 735 | ``` 736 | # 无效的 TOML 文档 737 | [[fruit]] 738 | name = "苹果" 739 | 740 | [[fruit.variety]] 741 | name = "蛇果" 742 | 743 | # 这个表与之前的表冲突了 744 | [fruit.variety] 745 | name = "澳洲青苹" 746 | ``` 747 | 748 | 你也可以适当使用行内表: 749 | 750 | ```toml 751 | points = [ { x = 1, y = 2, z = 3 }, 752 | { x = 7, y = 8, z = 9 }, 753 | { x = 2, y = 4, z = 8 } ] 754 | ``` 755 | 756 | [文件扩展名](#user-content-filename-extension)  757 | ------------ 758 | 759 | TOML 文件应当使用 `.toml` 扩展名。 760 | 761 | [MIME 类型](#user-content-mime-type)  762 | ----------- 763 | 764 | 在互联网上传输 TOML 文件时,恰当的 MIME 类型是 `application/toml`。 765 | 766 | [与其它格式的比较](#user-content-comparison-with-other-formats)  767 | ------------------ 768 | 769 | 某种程度上讲 TOML 与 JSON 非常相似:简单,规则明确,并且宜于转化成普适的数据类型。 770 | JSON 适合序列化由计算机程序读写的数据。 771 | TOML 与 JSON 的不同之处是它的重点在于宜于人类读写。 772 | 注释就是一个好例子:当数据在程序之间传送时,它们并无卵用,但是在手工编辑的配置文件中非常有帮助。 773 | 774 | YAML 格式就像 TOML 一样,是以配置文件为导向的。 775 | 然而……因为种种原因,YAML 是一种过于复杂的解决方案。 776 | TOML 旨在简易,这是一个在 YAML 的规范中于肉眼不可见的目标→https://www.yaml.org/spec/1.2/spec.html 777 | 778 | INI 格式也常见于配置文件。 779 | 然而……这个格式并无统一标准,并且通常不料理超过一或两层的嵌套。 780 | 781 | [参与](#user-content-get-involved)  782 | ------ 783 | 784 | 欢迎帮助文档、问题反馈、修缮合并,以及其它一切形式的贡献! 785 | 786 | [百科](#user-content-wiki)  787 | ------ 788 | 789 | 我们有一个[官方 TOML 百科](https://github.com/toml-lang/toml/wiki)收录了以下内容: 790 | 791 | * 使用了 TOML 的项目 792 | * 可用实现清单 793 | * 验证程序 794 | * TOML 解码器和编码器的语言无关测试套件 795 | * 编辑器支持 796 | * 编码器 797 | * 格式转换器 798 | 799 | 请点进去看一下,如果你想要阅览或加入其中。 800 | 感谢你成为 TOML 社区的一员! 801 | -------------------------------------------------------------------------------- /toml-v1.0.0.md: -------------------------------------------------------------------------------- 1 | ![TOML Logo](logos/toml-200.png) 2 | 3 | TOML v1.0.0 4 | =========== 5 | 6 | 汤小明的小巧明晰语言。 7 | 8 | 作者:[汤姆·普雷斯顿—维尔纳](https://github.com/mojombo)、[Pradyun Gedam](https://github.com/pradyunsg) 等人。[龙腾道](https://github.com/LongTengDao)译。 9 | 10 | 宗旨 11 | ---- 12 | 13 | TOML 旨在成为一个语义显著而易于阅读的最低限度的配置文件格式。 14 | TOML 被设计地能够无歧义地转化为哈希表。 15 | TOML 应当能简单地解析成形形色色的语言中的数据结构。 16 | 17 | 目录 18 | ---- 19 | 20 | - [说明](#user-content-spec) 21 | - [注释](#user-content-comment) 22 | - [键值对](#user-content-keyvalue-pair) 23 | - [键名](#user-content-keys) 24 | - [字符串](#user-content-string) 25 | - [整数](#user-content-integer) 26 | - [浮点数](#user-content-float) 27 | - [布尔值](#user-content-boolean) 28 | - [坐标日期时刻](#user-content-offset-date-time) 29 | - [各地日期时刻](#user-content-local-date-time) 30 | - [各地日期](#user-content-local-date) 31 | - [各地时刻](#user-content-local-time) 32 | - [数组](#user-content-array) 33 | - [表](#user-content-table) 34 | - [行内表](#user-content-inline-table) 35 | - [表数组](#user-content-array-of-tables) 36 | - [文件扩展名](#user-content-filename-extension) 37 | - [MIME 类型](#user-content-mime-type) 38 | - [ABNF 语法](#user-content-abnf-grammar) 39 | 40 | [说明](#user-content-spec)  41 | ------ 42 | 43 | * TOML 是大小写敏感的。 44 | * TOML 文件必须是有效的 UTF-8 编码的 Unicode 文档。 45 | * 空白的意思是 Tab(0x09)或空格(0x20)。 46 | * 换行的意思是 LF(0x0A)或 CRLF(0x0D 0x0A)。 47 | 48 | [注释](#user-content-comment)  49 | ------ 50 | 51 | 井号将此行剩下的部分标记为注释,除非它在字符串中。 52 | 53 | ```toml 54 | # 这是一个全行注释 55 | key = "value" # 这是一个行末注释 56 | another = "# 这不是一个注释" 57 | ``` 58 | 59 | 除 Tab 以外的控制字符(U+0000 至 U+0008,U+000A 至 U+001F,U+007F)不允许出现在注释中。 60 | 61 | [键值对](#user-content-keyvalue-pair)  62 | -------- 63 | 64 | TOML 文档最基本的构成区块是键值对。 65 | 66 | 键名在等号的左边而值在右边。 67 | 键名和键值周围的空白会被忽略。 68 | 键、等号和值必须在同一行(不过有些值可以跨多行)。 69 | 70 | ```toml 71 | key = "value" 72 | ``` 73 | 74 | 值必须是下述类型之一。 75 | 76 | - [字符串](#user-content-string) 77 | - [整数](#user-content-integer) 78 | - [浮点数](#user-content-float) 79 | - [布尔值](#user-content-boolean) 80 | - [坐标日期时刻](#user-content-offset-date-time) 81 | - [各地日期时刻](#user-content-local-date-time) 82 | - [各地日期](#user-content-local-date) 83 | - [各地时刻](#user-content-local-time) 84 | - [数组](#user-content-array) 85 | - [行内表](#user-content-inline-table) 86 | 87 | 不指定值是错误的。 88 | 89 | ```toml 90 | key = # 错误 91 | ``` 92 | 93 | 键值对后必须换行(或结束文件)。 94 | (例外见[行内表](#user-content-inline-table)) 95 | 96 | ``` 97 | first = "汤姆" last = "普雷斯顿—维尔纳" # 错误 98 | ``` 99 | 100 | [键名](#user-content-keys)  101 | ------ 102 | 103 | 键名可以是裸露的,引号引起来的,或点分隔的。 104 | 105 | **裸键**只能包含 ASCII 字母,ASCII 数字,下划线和短横线(`A-Za-z0-9_-`)。 106 | 注意裸键允许仅由纯 ASCII 数字构成,例如 `1234`,但是是被理解为字符串的。 107 | 108 | ```toml 109 | key = "value" 110 | bare_key = "value" 111 | bare-key = "value" 112 | 1234 = "value" 113 | ``` 114 | 115 | **引号键**遵循与基础字符串或字面量字符串相同的规则并允许你使用更为广泛的键名。 116 | 除非明显必要,使用裸键方为最佳实践。 117 | 118 | ```toml 119 | "127.0.0.1" = "value" 120 | "character encoding" = "value" 121 | "ʎǝʞ" = "value" 122 | 'key2' = "value" 123 | 'quoted "value"' = "value" 124 | ``` 125 | 126 | 裸键不能为空,但空引号键是允许的(虽然不建议如此)。 127 | 128 | ```toml 129 | = "没有键名" # 错误 130 | "" = "空" # 正确但不鼓励 131 | '' = '空' # 正确但不鼓励 132 | ``` 133 | 134 | **点分隔键**是一系列通过点相连的裸键或引号键。 135 | 这允许了你将相近属性放在一起: 136 | 137 | ```toml 138 | name = "橙子" 139 | physical.color = "橙色" 140 | physical.shape = "圆形" 141 | site."google.com" = true 142 | ``` 143 | 144 | 这在 JSON 那儿,是以下结构: 145 | 146 | ```json 147 | { 148 | "name": "橙子", 149 | "physical": { 150 | "color": "橙色", 151 | "shape": "圆形" 152 | }, 153 | "site": { 154 | "google.com": true 155 | } 156 | } 157 | ``` 158 | 159 | 有关点分隔键定义表的详细信息,请参阅后文[表](#user-content-table)一节。 160 | 161 | 点分隔符周围的空白会被忽略。 162 | 不过,最佳实践是不要使用任何不必要的空白。 163 | 164 | ```toml 165 | fruit.name = "香蕉" # 这是最佳实践 166 | fruit. color = "黄色" # 等同于 fruit.color 167 | fruit . flavor = "香蕉" # 等同于 fruit.flavor 168 | ``` 169 | 170 | 缩进被作为空白对待而被忽略。 171 | 172 | 多次定义同一个键是不行的。 173 | 174 | ``` 175 | # 不要这样做 176 | name = "汤姆" 177 | name = "Pradyun" 178 | ``` 179 | 180 | 注意裸键和引号键是等价的: 181 | 182 | ``` 183 | # 这是不可行的 184 | spelling = "favorite" 185 | "spelling" = "favourite" 186 | ``` 187 | 188 | 只要一个键还没有被直接定义过,你就仍可以对它和它下属的键名赋值。 189 | 190 | ``` 191 | # 这使“fruit”键作为表存在。 192 | fruit.apple.smooth = true 193 | 194 | # 所以接下来你可以像中这样对“fruit”表添加内容: 195 | fruit.orange = 2 196 | ``` 197 | 198 | ``` 199 | # 以下是错误的 200 | 201 | # 这将 fruit.apple 的值定义为一个整数。 202 | fruit.apple = 1 203 | 204 | # 但接下来这将 fruit.apple 像表一样对待了。 205 | # 整数不能变成表。 206 | fruit.apple.smooth = true 207 | ``` 208 | 209 | 不鼓励跳跃式地定义点分隔键。 210 | 211 | ```toml 212 | # 有效但不鼓励 213 | 214 | apple.type = "水果" 215 | orange.type = "水果" 216 | 217 | apple.skin = "薄" 218 | orange.skin = "厚" 219 | 220 | apple.color = "红" 221 | orange.color = "橙" 222 | ``` 223 | 224 | ```toml 225 | # 建议 226 | 227 | apple.type = "水果" 228 | apple.skin = "薄" 229 | apple.color = "红" 230 | 231 | orange.type = "水果" 232 | orange.skin = "厚" 233 | orange.color = "红" 234 | ``` 235 | 236 | 由于裸键可以仅由 ASCII 整数组成,所以可能写出看起来像浮点数、但实际上是两部分的点分隔键。 237 | 除非你有充分的理由(基本不太会),否则不要这样做。 238 | 239 | ```toml 240 | 3.14159 = "派" 241 | ``` 242 | 243 | 上面的 TOML 对应以下 JSON。 244 | 245 | ```json 246 | { "3": { "14159": "派" } } 247 | ``` 248 | 249 | [字符串](#user-content-string)  250 | -------- 251 | 252 | 共有四种方式来表示字符串:基础式的,多行基础式的,字面量式的,和多行字面量式的。 253 | 所有字符串都只能包含有效的 UTF-8 字符。 254 | 255 | **基础字符串**由引号(`"`)包裹。 256 | 任何 Unicode 字符都可以使用,除了那些必须转义的:引号,反斜杠,以及除 Tab 外的控制字符(U+0000 至 U+0008,U+000A 至 U+001F,U+007F)。 257 | 258 | ```toml 259 | str = "我是一个字符串。\"你可以把我引起来\"。姓名\tJos\u00E9\n位置\t旧金山。" 260 | ``` 261 | 262 | 为了方便,一些流行的字符有其简便转义写法。 263 | 264 | ``` 265 | \b - backspace (U+0008) 266 | \t - tab (U+0009) 267 | \n - linefeed (U+000A) 268 | \f - form feed (U+000C) 269 | \r - carriage return (U+000D) 270 | \" - quote (U+0022) 271 | \\ - backslash (U+005C) 272 | \uXXXX - unicode (U+XXXX) 273 | \UXXXXXXXX - unicode (U+XXXXXXXX) 274 | ``` 275 | 276 | 任何 Unicode 字符都可以用 `\uXXXX` 或 `\UXXXXXXXX` 的形式来转义。 277 | 转义码必须是有效的 Unicode [标量值](https://unicode.org/glossary/#unicode_scalar_value)。 278 | 279 | 所有上面未列出的其它转义序列都是保留的;如果用了,TOML 应当产生错误。 280 | 281 | 有时你需要表示一小篇文本(例如译文)或者想要对非常长的字符串进行折行。 282 | TOML 对此进行了简化。 283 | 284 | **多行基础字符串**由三个引号包裹,允许折行。 285 | 紧随开头引号的那个换行会被去除。 286 | 其它空白和换行会被原样保留。 287 | 288 | ```toml 289 | str1 = """ 290 | 玫瑰是红色的 291 | 紫罗兰是蓝色的""" 292 | ``` 293 | 294 | TOML 解析器可以相对灵活地解析成对所在平台有效的换行字符。 295 | 296 | ```toml 297 | # 在 Unix 系统,上面的多行字符串可能等同于: 298 | str2 = "玫瑰是红色的\n紫罗兰是蓝色的" 299 | 300 | # 在 Windows 系统,它可能等价于: 301 | str3 = "玫瑰是红色的\r\n紫罗兰是蓝色的" 302 | ``` 303 | 304 | 想书写长字符串却不想引入无关空白,可以用“行末反斜杠”。 305 | 当一行的最后一个非空白字符是未被转义的 `\` 时,它会连同它后面的所有空白(包括换行)一起被去除,直到下一个非空白字符或结束引号为止。 306 | 所有对基础字符串有效的转义序列,对多行基础字符串也同样适用。 307 | 308 | ```toml 309 | # 下列字符串的每一个字节都完全相同: 310 | str1 = "那只敏捷的棕狐狸跳过了那只懒狗。" 311 | 312 | str2 = """ 313 | 那只敏捷的棕\ 314 | 315 | 316 | 狐狸跳过了\ 317 | 那只懒狗。""" 318 | 319 | str3 = """\ 320 | 那只敏捷的棕\ 321 | 狐狸跳过了\ 322 | 那只懒狗。\ 323 | """ 324 | ``` 325 | 326 | 任何 Unicode 字符都可以使用,除了那些必须被转义的:反斜杠和除 Tab、换行符、回车符外的控制字符(U+0000 至 U+0008,U+000B,U+000C,U+000E 至 U+001F,U+007F)。 327 | 328 | 你可以在多行基础字符串内的任何地方写一个引号或两个毗连的引号。 329 | 它们也可以写在紧邻界分符内的位置。 330 | 331 | ```toml 332 | str4 = """这有两个引号:""。够简单。""" 333 | # str5 = """这有两个引号:"""。""" # 错误 334 | str5 = """这有三个引号:""\"。""" 335 | str6 = """这有十五个引号:""\"""\"""\"""\"""\"。""" 336 | 337 | # "这,"她说,"只是个无意义的条款。" 338 | str7 = """"这,"她说,"只是个无意义的条款。"""" 339 | ``` 340 | 341 | 如果你常常要指定 Windows 路径或正则表达式,那么必须转义反斜杠就马上成为啰嗦而易错的了。 342 | 为了帮助搞定这点,TOML 支持字面量字符串,它完全不允许转义。 343 | 344 | **字面量字符串**由单引号包裹。 345 | 类似于基础字符串,他们只能表现为单行: 346 | 347 | ```toml 348 | # 所见即所得。 349 | winpath = 'C:\Users\nodejs\templates' 350 | winpath2 = '\\ServerX\admin$\system32\' 351 | quoted = '汤姆·"达布斯"·普雷斯顿—维尔纳' 352 | regex = '<\i\c*\s*>' 353 | ``` 354 | 355 | 由于没有转义,无法在由单引号包裹的字面量字符串中写入单引号。 356 | 万幸,TOML 支持一种多行版本的字面量字符串来解决这个问题。 357 | 358 | **多行字面量字符串**两侧各有三个单引号来包裹,允许换行。 359 | 类似于字面量字符串,无论任何转义都不存在。 360 | 紧随开始标记的那个换行会被剔除。 361 | 开始结束标记之间的所有其它内容会原样对待。 362 | 363 | ```toml 364 | regex2 = '''I [dw]on't need \d{2} apples''' 365 | lines = ''' 366 | 原始字符串中的 367 | 第一个换行被剔除了。 368 | 所有其它空白 369 | 都保留了。 370 | ''' 371 | ``` 372 | 373 | 你可以在多行字面量字符串中的任何位置写一个或两个单引号,但三个以上的单引号序列不可以。 374 | 375 | ```toml 376 | quot15 = '''这有十五个引号:"""""""""""""""''' 377 | 378 | # apos15 = '''这有十五个撇号:'''''''''''''''''' # 错误 379 | apos15 = "这有十五个撇号:'''''''''''''''" 380 | 381 | # '那,'她说,'仍然没有意义。' 382 | str = ''''那,'她说,'仍然没有意义。'''' 383 | ``` 384 | 385 | 除 Tab 以外的所有控制字符都不允许出现在字面量字符串中。 386 | 因此,对于二进制数据,建议你使用 Base64 或其它合适的 ASCII 或 UTF-8 编码。 387 | 对那些编码的处理方式,将交由应用程序自己来确定。 388 | 389 | [整数](#user-content-integer)  390 | ------ 391 | 392 | 整数是纯数字。 393 | 正数可以有加号前缀。 394 | 负数的前缀是减号。 395 | 396 | ```toml 397 | int1 = +99 398 | int2 = 42 399 | int3 = 0 400 | int4 = -17 401 | ``` 402 | 403 | 对于大数,你可以在数字之间用下划线来增强可读性。 404 | 每个下划线两侧必须至少有一个数字。 405 | 406 | ```toml 407 | int5 = 1_000 408 | int6 = 5_349_221 409 | int7 = 53_49_221 # 印度记数体系分组 410 | int8 = 1_2_3_4_5 # 无误但不鼓励 411 | ``` 412 | 413 | 前导零是不允许的。 414 | 整数值 `-0` 与 `+0` 是有效的,并等同于无前缀的零。 415 | 416 | 非负整数值也可以用十六进制、八进制或二进制来表示。 417 | 在这些格式中,`+` 不被允许,而(前缀后的)前导零是允许的。 418 | 十六进制值大小写不敏感。 419 | 数字间的下划线是允许的(但不能存在于前缀和值之间)。 420 | 421 | ```toml 422 | # 带有 `0x` 前缀的十六进制 423 | hex1 = 0xDEADBEEF 424 | hex2 = 0xdeadbeef 425 | hex3 = 0xdead_beef 426 | 427 | # 带有 `0o` 前缀的八进制 428 | oct1 = 0o01234567 429 | oct2 = 0o755 # 对于表示 Unix 文件权限很有用 430 | 431 | # 带有 `0b` 前缀的二进制 432 | bin1 = 0b11010110 433 | ``` 434 | 435 | 任何 64 位有符号整数(从 −2^63 到 2^63−1)都应当被接受并无损处理。 436 | 如果无法无损表现某个整数,则必须抛出错误。 437 | 438 | [浮点数](#user-content-float)  439 | -------- 440 | 441 | 浮点数应当被实现为 IEEE 754 binary64 值。 442 | 443 | 一个浮点数由一个整数部分(遵从与十进制整数值相同的规则)后跟上一个小数部分和/或一个指数部分组成。 444 | 如果小数部分和指数部分兼有,那小数部分必须在指数部分前面。 445 | 446 | ```toml 447 | # 小数 448 | flt1 = +1.0 449 | flt2 = 3.1415 450 | flt3 = -0.01 451 | 452 | # 指数 453 | flt4 = 5e+22 454 | flt5 = 1e06 455 | flt6 = -2E-2 456 | 457 | # 都有 458 | flt7 = 6.626e-34 459 | ``` 460 | 461 | 小数部分是一个小数点后跟一个或多个数字。 462 | 463 | 一个指数部分是一个 E(大小写均可)后跟一个整数部分(遵从与十进制整数值相同的规则,但可以包含前导零)。 464 | 465 | 小数点,如果有用到的话,每侧必须紧邻至少一个数字。 466 | 467 | ``` 468 | # 非法的浮点数 469 | invalid_float_1 = .7 470 | invalid_float_2 = 7. 471 | invalid_float_3 = 3.e+20 472 | ``` 473 | 474 | 与整数相似,你可以使用下划线来增强可读性。 475 | 每个下划线必须被至少一个数字围绕。 476 | 477 | ```toml 478 | flt8 = 224_617.445_991_228 479 | ``` 480 | 481 | 浮点数值 `-0.0` 与 `+0.0` 是有效的,并且应当遵从 IEEE 754。 482 | 483 | 特殊浮点值也能够表示。 484 | 它们是小写的。 485 | 486 | ```toml 487 | # 无穷 488 | sf1 = inf # 正无穷 489 | sf2 = +inf # 正无穷 490 | sf3 = -inf # 负无穷 491 | 492 | # 非数 493 | sf4 = nan # 实际上对应信号非数码还是静默非数码,取决于实现 494 | sf5 = +nan # 等同于 `nan` 495 | sf6 = -nan # 正确,实际码取决于实现 496 | ``` 497 | 498 | [布尔值](#user-content-boolean)  499 | -------- 500 | 501 | 布尔值就是你所惯用的那样。 502 | 要小写。 503 | 504 | ```toml 505 | bool1 = true 506 | bool2 = false 507 | ``` 508 | 509 | [坐标日期时刻](#user-content-offset-date-time)  510 | -------------- 511 | 512 | 要明确无误地表示世上的一个特定时间,你可以使用指定了时区偏移量的 [RFC 3339](https://tools.ietf.org/html/rfc3339) 格式的日期时刻。 513 | 514 | ```toml 515 | odt1 = 1979-05-27T07:32:00Z 516 | odt2 = 1979-05-27T00:32:00-07:00 517 | odt3 = 1979-05-27T00:32:00.999999-07:00 518 | ``` 519 | 520 | 出于可读性的目的,你可以用一个空格字符替代日期和时刻之间的 T(RFC 3339 的第 5.6 节中允许了这样做)。 521 | 522 | ```toml 523 | odt4 = 1979-05-27 07:32:00Z 524 | ``` 525 | 526 | 毫秒级的精度是必须的。 527 | 更高精度的小数秒取决于实现。 528 | 如果它的值超出了实现所支持的精度,那多余的部分必须被舍弃,而不能四舍五入。 529 | 530 | [各地日期时刻](#user-content-local-date-time)  531 | -------------- 532 | 533 | 如果你省略了 [RFC 3339](https://tools.ietf.org/html/rfc3339) 日期时刻中的时区偏移量,这表示该日期时刻的使用并不涉及时区偏移。 534 | 在没有其它信息的情况下,并不知道它究竟该被转化成世上的哪一刻。 535 | 如果仍被要求转化,那结果将取决于实现。 536 | 537 | ```toml 538 | ldt1 = 1979-05-27T07:32:00 539 | ldt2 = 1979-05-27T00:32:00.999999 540 | ``` 541 | 542 | 毫秒级的精度是必须的。 543 | 更高精度的小数秒取决于实现。 544 | 如果它的值超出了实现所支持的精度,那多余的部分必须被舍弃,而不能四舍五入。 545 | 546 | [各地日期](#user-content-local-date)  547 | ---------- 548 | 549 | 如果你只写了 [RFC 3339](https://tools.ietf.org/html/rfc3339) 日期时刻中的日期部分,那它表示一整天,同时也不涉及时区偏移。 550 | 551 | ```toml 552 | ld1 = 1979-05-27 553 | ``` 554 | 555 | [各地时刻](#user-content-local-time)  556 | ---------- 557 | 558 | 如果你只写了 [RFC 3339](https://tools.ietf.org/html/rfc3339) 日期时刻中的时刻部分,它将只表示一天之中的那个时刻,而与任何特定的日期无关、亦不涉及时区偏移。 559 | 560 | ```toml 561 | lt1 = 07:32:00 562 | lt2 = 00:32:00.999999 563 | ``` 564 | 565 | 毫秒级的精度是必须的。 566 | 更高精度的小数秒取决于实现。 567 | 如果它的值超出了实现所支持的精度,那多余的部分必须被舍弃,而不能四舍五入。 568 | 569 | [数组](#user-content-array)  570 | ------ 571 | 572 | 数组是内含值的方括号。 573 | 空白会被忽略。 574 | 子元素由逗号分隔。 575 | 数组可以包含与键值对所允许的相同数据类型的值。 576 | 可以混合不同类型的值。 577 | 578 | ```toml 579 | integers = [ 1, 2, 3 ] 580 | colors = [ "红", "黄", "绿" ] 581 | nested_array_of_ints = [ [ 1, 2 ], [3, 4, 5] ] 582 | nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] 583 | string_array = [ "所有的", '字符串', """是相同的""", '''类型''' ] 584 | 585 | # 允许混合类型的数组 586 | numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] 587 | contributors = [ 588 | "Foo Bar ", 589 | { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } 590 | ] 591 | ``` 592 | 593 | 数组可以跨行。 594 | 数组的最后一个值后面可以有终逗号(也称为尾逗号)。 595 | 值、逗号、结束括号前可以存在任意数量的换行和注释。 596 | 数组值和逗号之间的缩进被作为空白对待而被忽略。 597 | 598 | ```toml 599 | integers2 = [ 600 | 1, 2, 3 601 | ] 602 | 603 | integers3 = [ 604 | 1, 605 | 2, # 这是可以的 606 | ] 607 | ``` 608 | 609 | [表](#user-content-table)  610 | ---- 611 | 612 | 表(也被称为哈希表或字典)是键值对的集合。 613 | 它们由表头定义,连同方括号作为单独的行出现。 614 | 看得出表头不同于数组,因为数组只有值。 615 | 616 | ```toml 617 | [table] 618 | ``` 619 | 620 | 在它下方,直至下一个表头或文件结束,都是这个表的键值对。 621 | 表不保证保持键值对的指定顺序。 622 | 623 | ```toml 624 | [table-1] 625 | key1 = "some string" 626 | key2 = 123 627 | 628 | [table-2] 629 | key1 = "another string" 630 | key2 = 456 631 | ``` 632 | 633 | 表名的规则与键名相同(见前文[键名](#user-content-keys)定义)。 634 | 635 | ```toml 636 | [dog."tater.man"] 637 | type.name = "哈巴狗" 638 | ``` 639 | 640 | 这在 JSON 那儿,是以下结构: 641 | 642 | ```json 643 | { "dog": { "tater.man": { "type": { "name": "哈巴狗" } } } } 644 | ``` 645 | 646 | 键名周围的空格会被忽略。 647 | 然而,最佳实践还是不要有任何多余的空白。 648 | 649 | ```toml 650 | [a.b.c] # 这是最佳实践 651 | [ d.e.f ] # 等同于 [d.e.f] 652 | [ g . h . i ] # 等同于 [g.h.i] 653 | [ j . "ʞ" . 'l' ] # 等同于 [j."ʞ".'l'] 654 | ``` 655 | 656 | 缩进被作为空白对待而被忽略。 657 | 658 | 你不必层层完整地写出你不想写的所有途径的父表。 659 | TOML 知道该怎么办。 660 | 661 | ```toml 662 | # [x] 你 663 | # [x.y] 不 664 | # [x.y.z] 需要这些 665 | [x.y.z.w] # 来让这生效 666 | 667 | [x] # 后置父表定义是可以的 668 | ``` 669 | 670 | 空表是允许的,只要里面没有键值对就行了。 671 | 672 | 类似于键名,你不能重复定义一个表。 673 | 这样做是错误的。 674 | 675 | ``` 676 | # 不要这样做 677 | 678 | [fruit] 679 | apple = "红色" 680 | 681 | [fruit] 682 | orange = "橙色" 683 | ``` 684 | 685 | ``` 686 | # 也不要这样做 687 | 688 | [fruit] 689 | apple = "红色" 690 | 691 | [fruit.apple] 692 | texture = "光滑" 693 | ``` 694 | 695 | 不鼓励无序地定义表。 696 | 697 | ```toml 698 | # 正确但不鼓励 699 | [fruit.apple] 700 | [animal] 701 | [fruit.orange] 702 | ``` 703 | 704 | ```toml 705 | # 推荐 706 | [fruit.apple] 707 | [fruit.orange] 708 | [animal] 709 | ``` 710 | 711 | 顶层表,又被称为根表,于文档开始处开始并在第一个表头(或文件结束处)前结束。 712 | 不同于其它表,它没有名字且无法后置。 713 | 714 | ```toml 715 | # 顶层表开始。 716 | name = "Fido" 717 | breed = "哈巴狗" 718 | 719 | # 顶层表结束。 720 | [owner] 721 | name = "Regina Dogman" 722 | member_since = 1999-08-04 723 | ``` 724 | 725 | 点分隔键为最后一个键名前的每个键名创建并定义一个表,倘若这些表尚未被创建的话。 726 | 727 | ```toml 728 | fruit.apple.color = "红色" 729 | # 定义一个名为 fruit 的表 730 | # 定义一个名为 fruit.apple 的表 731 | 732 | fruit.apple.taste.sweet = true 733 | # 定义一个名为 fruit.apple.taste 的表 734 | # fruit 和 fruit.apple 已经创建过了 735 | ``` 736 | 737 | 由于表不能定义多于一次,不允许使用 `[table]` 头重定义这样的表。 738 | 同样地,使用点分隔键来重定义已经以 `[table]` 形式定义过的表也是不允许的。 739 | 不过,`[table]` 形式可以被用来定义通过点分隔键定义的表中的子表。 740 | 741 | ```toml 742 | [fruit] 743 | apple.color = "红色" 744 | apple.taste.sweet = true 745 | 746 | # [fruit.apple] # 错误 747 | # [fruit.apple.taste] # 错误 748 | 749 | [fruit.apple.texture] # 你可以添加子表 750 | smooth = true 751 | ``` 752 | 753 | [行内表](#user-content-inline-table)  754 | -------- 755 | 756 | 行内表提供了一种更为紧凑的语法来表示表。 757 | 对于否则就很啰嗦的成组数据,这尤其有用。 758 | 行内表被完整地定义在花括号之中:`{` 和 `}`。 759 | 括号中,可以出现零或更多个以逗号分隔的键值对。 760 | 键值对采取与标准表中的键值对相同的形式。 761 | 什么类型的值都可以,包括行内表。 762 | 763 | 行内表得出现在同一行内。 764 | 行内表中,最后一对键值对后不允许终逗号(也称为尾逗号)。 765 | 不允许花括号中出现任何换行,除非在值中它们有效。 766 | 即便如此,也强烈不建议把一个行内表搞成纵跨多行的样子。 767 | 如果你发现自己真的需要,那意味着你应该使用标准表。 768 | 769 | ```toml 770 | name = { first = "汤姆", last = "普雷斯顿—维尔纳" } 771 | point = { x = 1, y = 2 } 772 | animal = { type.name = "哈巴狗" } 773 | ``` 774 | 775 | 上述行内表等同于下面的标准表定义: 776 | 777 | ```toml 778 | [name] 779 | first = "汤姆" 780 | last = "普雷斯顿—维尔纳" 781 | 782 | [point] 783 | x = 1 784 | y = 2 785 | 786 | [animal] 787 | type.name = "哈巴狗" 788 | ``` 789 | 790 | 行内表是独立自足的,在内部定义全部的键与子表。 791 | 不能在括号以外的地方,再添加键与子表。 792 | 793 | ```toml 794 | [product] 795 | type = { name = "钉子" } 796 | # type.edible = false # 错误 797 | ``` 798 | 799 | 类似地,行内表不能被用来向一个已定义的表添加键或子表。 800 | 801 | ```toml 802 | [product] 803 | type.name = "钉子" 804 | # type = { edible = false } # 错误 805 | ``` 806 | 807 | [表数组](#user-content-array-of-tables)  808 | -------- 809 | 810 | 最后一个还没讲到的语法允许你写表数组。 811 | 这可以通过把表名写在双方括号里的表头来表示。 812 | 表头的第一例定义了这个数组及其首个表元素,而后续的每个则在该数组中创建并定义一个新的表元素。 813 | 这些表按出现顺序插入该数组。 814 | 815 | ```toml 816 | [[products]] 817 | name = "锤子" 818 | sku = 738594937 819 | 820 | [[products]] # 数组里的空表 821 | 822 | [[products]] 823 | name = "钉子" 824 | sku = 284758393 825 | 826 | color = "灰色" 827 | ``` 828 | 829 | 这在 JSON 那儿,是以下结构。 830 | 831 | ```json 832 | { 833 | "products": [ 834 | { "name": "锤子", "sku": 738594937 }, 835 | { }, 836 | { "name": "钉子", "sku": 284758393, "color": "gray" } 837 | ] 838 | } 839 | ``` 840 | 841 | 任何对表数组的引用都指向该数组里最近定义的表元素。 842 | 这允许你在最近的表内定义子表,甚至子表数组。 843 | 844 | ```toml 845 | [[fruits]] 846 | name = "苹果" 847 | 848 | [fruits.physical] # 子表 849 | color = "红色" 850 | shape = "圆形" 851 | 852 | [[fruits.varieties]] # 嵌套表数组 853 | name = "蛇果" 854 | 855 | [[fruits.varieties]] 856 | name = "澳洲青苹" 857 | 858 | [[fruits]] 859 | name = "香蕉" 860 | 861 | [[fruits.varieties]] 862 | name = "车前草" 863 | ``` 864 | 865 | 上述 TOML 对应下面的 JSON。 866 | 867 | ```json 868 | { 869 | "fruits": [ 870 | { 871 | "name": "苹果", 872 | "physical": { 873 | "color": "红色", 874 | "shape": "圆形" 875 | }, 876 | "varieties": [ 877 | { "name": "蛇果" }, 878 | { "name": "澳洲青苹" } 879 | ] 880 | }, 881 | { 882 | "name": "香蕉", 883 | "varieties": [ 884 | { "name": "车前草" } 885 | ] 886 | } 887 | ] 888 | } 889 | ``` 890 | 891 | 如果一个表或表数组的父级是一个数组元素,该元素必须在定义子级前先定义。 892 | 顺序颠倒的行为,必须在解析时报错。 893 | 894 | ``` 895 | # 错误的 TOML 文档 896 | [fruit.physical] # 子表,但它应该隶属于哪个父元素? 897 | color = "红色" 898 | shape = "圆形" 899 | 900 | [[fruit]] # 解析器必须在发现“fruit”是数组而非表时抛出错误 901 | name = "苹果" 902 | ``` 903 | 904 | 若试图向一个静态定义的数组追加内容,即便数组尚且为空,也必须在解析时报错。 905 | 906 | ``` 907 | # 错误的 TOML 文档 908 | fruits = [] 909 | 910 | [[fruits]] # 不允许 911 | ``` 912 | 913 | 若试图用已经确定为数组的名称定义表,必须在解析时报错。 914 | 将数组重定义为普通表的行为,也必须在解析时报错。 915 | 916 | ``` 917 | # 错误的 TOML 文档 918 | [[fruits]] 919 | name = "苹果" 920 | 921 | [[fruits.varieties]] 922 | name = "蛇果" 923 | 924 | # 错误:该表与之前的表数组相冲突 925 | [fruits.varieties] 926 | name = "澳洲青苹" 927 | 928 | [fruits.physical] 929 | color = "红色" 930 | shape = "圆形" 931 | 932 | # 错误:该表数组与之前的表相冲突 933 | [[fruits.physical]] 934 | color = "绿色" 935 | ``` 936 | 937 | 你也可以适当使用行内表: 938 | 939 | ```toml 940 | points = [ { x = 1, y = 2, z = 3 }, 941 | { x = 7, y = 8, z = 9 }, 942 | { x = 2, y = 4, z = 8 } ] 943 | ``` 944 | 945 | [文件扩展名](#user-content-filename-extension)  946 | ------------ 947 | 948 | TOML 文件应当使用 `.toml` 扩展名。 949 | 950 | [MIME 类型](#user-content-mime-type)  951 | ----------- 952 | 953 | 在互联网上传输 TOML 文件时,恰当的 MIME 类型是 `application/toml`。 954 | 955 | [ABNF 语法](#user-content-abnf-grammar)  956 | ----------- 957 | 958 | TOML 语法的严谨说明,由一个 [ABNF 文件][abnf]另行提供。 959 | 960 | [abnf]: https://github.com/LongTengDao/TOML/blob/%E9%BE%99%E8%85%BE%E9%81%93-%E8%AF%91/toml.abnf 961 | -------------------------------------------------------------------------------- /toml.abnf: -------------------------------------------------------------------------------- 1 | ;; 该文档描述了 TOML 的语法,使用的是 ABNF 格式(定义于 RFC 5234——https://www.ietf.org/rfc/rfc5234.txt)。 2 | ;; 3 | ;; 所有有效的 TOML 文档都符合该描述,然而根据辅助性文本说明中描述的语义需要拒绝某些错误的文档。 4 | 5 | ;; 可以通过 instaparse 交互式地尝试该语法。 6 | ;; http://instaparse.mojombo.com/ 7 | ;; 8 | ;; 要做到这一点,在右下角,点击 Options 并将 `:input-format` 改为 ':abnf'。 9 | ;; 然后将整个 ABNF 文档粘贴到语法输入框(在 Options 上方)。 10 | ;; 然后您可以将一份 TOML 文档小样输入或粘贴到左侧米黄色的框中。 11 | ;; Tada! 12 | 13 | ;; 总结构 14 | 15 | toml = expression *( newline expression ) 16 | 17 | expression = ws [ comment ] 18 | expression =/ ws keyval ws [ comment ] 19 | expression =/ ws table ws [ comment ] 20 | 21 | ;; 空白 22 | 23 | ws = *wschar 24 | wschar = %x20 ; 空格 25 | wschar =/ %x09 ; 水平制表符 26 | 27 | ;; 换行 28 | 29 | newline = %x0A ; LF 30 | newline =/ %x0D.0A ; CRLF 31 | 32 | ;; 注释 33 | 34 | comment-start-symbol = %x23 ; # 35 | non-ascii = %x80-D7FF / %xE000-10FFFF 36 | non-eol = %x09 / %x20-7E / non-ascii 37 | 38 | comment = comment-start-symbol *non-eol 39 | 40 | ;; 键值对 41 | 42 | keyval = key keyval-sep val 43 | 44 | key = simple-key / dotted-key 45 | simple-key = quoted-key / unquoted-key 46 | 47 | unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ 48 | quoted-key = basic-string / literal-string 49 | dotted-key = simple-key 1*( dot-sep simple-key ) 50 | 51 | dot-sep = ws %x2E ws ; . 点 52 | keyval-sep = ws %x3D ws ; = 53 | 54 | val = string / boolean / array / inline-table / date-time / float / integer 55 | 56 | ;; 字符串 57 | 58 | string = ml-basic-string / basic-string / ml-literal-string / literal-string 59 | 60 | ;; 基础字符串 61 | 62 | basic-string = quotation-mark *basic-char quotation-mark 63 | 64 | quotation-mark = %x22 ; " 65 | 66 | basic-char = basic-unescaped / escaped 67 | basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii 68 | escaped = escape escape-seq-char 69 | 70 | escape = %x5C ; \ 71 | escape-seq-char = %x22 ; " 双引号 U+0022 72 | escape-seq-char =/ %x5C ; \ 反斜杠 U+005C 73 | escape-seq-char =/ %x62 ; b 退格符 U+0008 74 | escape-seq-char =/ %x66 ; f 换页符 U+000C 75 | escape-seq-char =/ %x6E ; n 换行符 U+000A 76 | escape-seq-char =/ %x72 ; r 回车符 U+000D 77 | escape-seq-char =/ %x74 ; t 制表符 U+0009 78 | escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX 79 | escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX 80 | 81 | ;; 多行基础字符串 82 | 83 | ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body 84 | ml-basic-string-delim 85 | ml-basic-string-delim = 3quotation-mark 86 | ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] 87 | 88 | mlb-content = mlb-char / newline / mlb-escaped-nl 89 | mlb-char = mlb-unescaped / escaped 90 | mlb-quotes = 1*2quotation-mark 91 | mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii 92 | mlb-escaped-nl = escape ws newline *( wschar / newline ) 93 | 94 | ;; 字面量字符串 95 | 96 | literal-string = apostrophe *literal-char apostrophe 97 | 98 | apostrophe = %x27 ; ' 单引号 99 | 100 | literal-char = %x09 / %x20-26 / %x28-7E / non-ascii 101 | 102 | ;; 多行字面量字符串 103 | 104 | ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body 105 | ml-literal-string-delim 106 | ml-literal-string-delim = 3apostrophe 107 | ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ] 108 | 109 | mll-content = mll-char / newline 110 | mll-char = %x09 / %x20-26 / %x28-7E / non-ascii 111 | mll-quotes = 1*2apostrophe 112 | 113 | ;; 整数 114 | 115 | integer = dec-int / hex-int / oct-int / bin-int 116 | 117 | minus = %x2D ; - 118 | plus = %x2B ; + 119 | underscore = %x5F ; _ 120 | digit1-9 = %x31-39 ; 1-9 121 | digit0-7 = %x30-37 ; 0-7 122 | digit0-1 = %x30-31 ; 0-1 123 | 124 | hex-prefix = %x30.78 ; 0x 125 | oct-prefix = %x30.6F ; 0o 126 | bin-prefix = %x30.62 ; 0b 127 | 128 | dec-int = [ minus / plus ] unsigned-dec-int 129 | unsigned-dec-int = DIGIT / digit1-9 1*( DIGIT / underscore DIGIT ) 130 | 131 | hex-int = hex-prefix HEXDIG *( HEXDIG / underscore HEXDIG ) 132 | oct-int = oct-prefix digit0-7 *( digit0-7 / underscore digit0-7 ) 133 | bin-int = bin-prefix digit0-1 *( digit0-1 / underscore digit0-1 ) 134 | 135 | ;; 浮点数 136 | 137 | float = float-int-part ( exp / frac [ exp ] ) 138 | float =/ special-float 139 | 140 | float-int-part = dec-int 141 | frac = decimal-point zero-prefixable-int 142 | decimal-point = %x2E ; . 143 | zero-prefixable-int = DIGIT *( DIGIT / underscore DIGIT ) 144 | 145 | exp = "e" float-exp-part 146 | float-exp-part = [ minus / plus ] zero-prefixable-int 147 | 148 | special-float = [ minus / plus ] ( inf / nan ) 149 | inf = %x69.6e.66 ; inf 150 | nan = %x6e.61.6e ; nan 151 | 152 | ;; 布尔值 153 | 154 | boolean = true / false 155 | 156 | true = %x74.72.75.65 ; true 157 | false = %x66.61.6C.73.65 ; false 158 | 159 | ;; 日期和时刻(按照 RFC 3339 中的定义) 160 | 161 | date-time = offset-date-time / local-date-time / local-date / local-time 162 | 163 | date-fullyear = 4DIGIT 164 | date-month = 2DIGIT ; 01-12 165 | date-mday = 2DIGIT ; 01-28,01-29,01-30,01-31 基于月/年 166 | time-delim = "T" / %x20 ; T,t,或空格 167 | time-hour = 2DIGIT ; 00-23 168 | time-minute = 2DIGIT ; 00-59 169 | time-second = 2DIGIT ; 00-58,00-59,00-60 基于闰秒规则 170 | time-secfrac = "." 1*DIGIT 171 | time-numoffset = ( "+" / "-" ) time-hour ":" time-minute 172 | time-offset = "Z" / time-numoffset 173 | 174 | partial-time = time-hour ":" time-minute ":" time-second [ time-secfrac ] 175 | full-date = date-fullyear "-" date-month "-" date-mday 176 | full-time = partial-time time-offset 177 | 178 | ;; 坐标日期时刻 179 | 180 | offset-date-time = full-date time-delim full-time 181 | 182 | ;; 各地日期时刻 183 | 184 | local-date-time = full-date time-delim partial-time 185 | 186 | ;; 各地日期 187 | 188 | local-date = full-date 189 | 190 | ;; 各地时刻 191 | 192 | local-time = partial-time 193 | 194 | ;; 数组 195 | 196 | array = array-open [ array-values ] ws-comment-newline array-close 197 | 198 | array-open = %x5B ; [ 199 | array-close = %x5D ; ] 200 | 201 | array-values = ws-comment-newline val ws-comment-newline array-sep array-values 202 | array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ] 203 | 204 | array-sep = %x2C ; , 逗号 205 | 206 | ws-comment-newline = *( wschar / [ comment ] newline ) 207 | 208 | ;; 表 209 | 210 | table = std-table / array-table 211 | 212 | ;; 标准表 213 | 214 | std-table = std-table-open key std-table-close 215 | 216 | std-table-open = %x5B ws ; [ 左方括号 217 | std-table-close = ws %x5D ; ] 右方括号 218 | 219 | ;; 行内表 220 | 221 | inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close 222 | 223 | inline-table-open = %x7B ws ; { 224 | inline-table-close = ws %x7D ; } 225 | inline-table-sep = ws %x2C ws ; , 逗号 226 | 227 | inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] 228 | 229 | ;; 数组表 230 | 231 | array-table = array-table-open key array-table-close 232 | 233 | array-table-open = %x5B.5B ws ; [[ 双左方括号 234 | array-table-close = ws %x5D.5D ; ]] 双右方括号 235 | 236 | ;; ABNF 的内置术语,为清楚起见重新陈列于此 237 | 238 | ALPHA = %x41-5A / %x61-7A ; A-Z / a-z 239 | DIGIT = %x30-39 ; 0-9 240 | HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" 241 | -------------------------------------------------------------------------------- /toml.md: -------------------------------------------------------------------------------- 1 | ![TOML Logo](logos/toml-200.png) 2 | 3 | TOML 4 | ==== 5 | 6 | 汤小明的小巧明晰语言。 7 | 8 | 作者:[汤姆·普雷斯顿—维尔纳](https://github.com/mojombo)、[Pradyun Gedam](https://github.com/pradyunsg) 等人。[龙腾道](https://github.com/LongTengDao)译。 9 | 10 | 宗旨 11 | ---- 12 | 13 | TOML 旨在成为一个语义显著而易于阅读的最低限度的配置文件格式。 14 | TOML 被设计地能够无歧义地转化为哈希表。 15 | TOML 应当能简单地解析成形形色色的语言中的数据结构。 16 | 17 | 目录 18 | ---- 19 | 20 | - [说明](#user-content-spec) 21 | - [注释](#user-content-comment) 22 | - [键值对](#user-content-keyvalue-pair) 23 | - [键名](#user-content-keys) 24 | - [字符串](#user-content-string) 25 | - [整数](#user-content-integer) 26 | - [浮点数](#user-content-float) 27 | - [布尔值](#user-content-boolean) 28 | - [坐标日期时刻](#user-content-offset-date-time) 29 | - [各地日期时刻](#user-content-local-date-time) 30 | - [各地日期](#user-content-local-date) 31 | - [各地时刻](#user-content-local-time) 32 | - [数组](#user-content-array) 33 | - [表](#user-content-table) 34 | - [行内表](#user-content-inline-table) 35 | - [表数组](#user-content-array-of-tables) 36 | - [文件扩展名](#user-content-filename-extension) 37 | - [MIME 类型](#user-content-mime-type) 38 | - [ABNF 语法](#user-content-abnf-grammar) 39 | 40 | [说明](#user-content-spec)  41 | ------ 42 | 43 | * TOML 是大小写敏感的。 44 | * TOML 文件必须是有效的 UTF-8 编码的 Unicode 文档。 45 | * 空白的意思是 Tab(0x09)或空格(0x20)。 46 | * 换行的意思是 LF(0x0A)或 CRLF(0x0D 0x0A)。 47 | 48 | [注释](#user-content-comment)  49 | ------ 50 | 51 | 井号将此行剩下的部分标记为注释,除非它在字符串中。 52 | 53 | ```toml 54 | # 这是一个全行注释 55 | key = "value" # 这是一个行末注释 56 | another = "# 这不是一个注释" 57 | ``` 58 | 59 | 除 Tab 以外的控制字符(U+0000 至 U+0008,U+000A 至 U+001F,U+007F)不允许出现在注释中。 60 | 61 | [键值对](#user-content-keyvalue-pair)  62 | -------- 63 | 64 | TOML 文档最基本的构成区块是键值对。 65 | 66 | 键名在等号的左边而值在右边。 67 | 键名和键值周围的空白会被忽略。 68 | 键、等号和值必须在同一行(不过有些值可以跨多行)。 69 | 70 | ```toml 71 | key = "value" 72 | ``` 73 | 74 | 值必须是下述类型之一。 75 | 76 | - [字符串](#user-content-string) 77 | - [整数](#user-content-integer) 78 | - [浮点数](#user-content-float) 79 | - [布尔值](#user-content-boolean) 80 | - [坐标日期时刻](#user-content-offset-date-time) 81 | - [各地日期时刻](#user-content-local-date-time) 82 | - [各地日期](#user-content-local-date) 83 | - [各地时刻](#user-content-local-time) 84 | - [数组](#user-content-array) 85 | - [行内表](#user-content-inline-table) 86 | 87 | 不指定值是错误的。 88 | 89 | ```toml 90 | key = # 错误 91 | ``` 92 | 93 | 键值对后必须换行(或结束文件)。 94 | (例外见[行内表](#user-content-inline-table)) 95 | 96 | ``` 97 | first = "汤姆" last = "普雷斯顿—维尔纳" # 错误 98 | ``` 99 | 100 | [键名](#user-content-keys)  101 | ------ 102 | 103 | 键名可以是裸露的,引号引起来的,或点分隔的。 104 | 105 | **裸键**只能包含 ASCII 字母,ASCII 数字,下划线和短横线(`A-Za-z0-9_-`)。 106 | 注意裸键允许仅由纯 ASCII 数字构成,例如 `1234`,但是是被理解为字符串的。 107 | 108 | ```toml 109 | key = "value" 110 | bare_key = "value" 111 | bare-key = "value" 112 | 1234 = "value" 113 | ``` 114 | 115 | **引号键**遵循与基础字符串或字面量字符串相同的规则并允许你使用更为广泛的键名。 116 | 除非明显必要,使用裸键方为最佳实践。 117 | 118 | ```toml 119 | "127.0.0.1" = "value" 120 | "character encoding" = "value" 121 | "ʎǝʞ" = "value" 122 | 'key2' = "value" 123 | 'quoted "value"' = "value" 124 | ``` 125 | 126 | 裸键不能为空,但空引号键是允许的(虽然不建议如此)。 127 | 多行字符串不能用于定义引号键。 128 | 129 | ```toml 130 | = "没有键名" # 错误 131 | """键""" = "不允许" # 错误 132 | "" = "空" # 正确但不鼓励 133 | '' = '空' # 正确但不鼓励 134 | ``` 135 | 136 | **点分隔键**是一系列通过点相连的裸键或引号键。 137 | 这允许了你将相近属性放在一起: 138 | 139 | ```toml 140 | name = "橙子" 141 | physical.color = "橙色" 142 | physical.shape = "圆形" 143 | site."google.com" = true 144 | ``` 145 | 146 | 这在 JSON 那儿,是以下结构: 147 | 148 | ```json 149 | { 150 | "name": "橙子", 151 | "physical": { 152 | "color": "橙色", 153 | "shape": "圆形" 154 | }, 155 | "site": { 156 | "google.com": true 157 | } 158 | } 159 | ``` 160 | 161 | 有关点分隔键定义表的详细信息,请参阅后文[表](#user-content-table)一节。 162 | 163 | 点分隔符周围的空白会被忽略。 164 | 不过,最佳实践是不要使用任何不必要的空白。 165 | 166 | ```toml 167 | fruit.name = "香蕉" # 这是最佳实践 168 | fruit. color = "黄色" # 等同于 fruit.color 169 | fruit . flavor = "香蕉" # 等同于 fruit.flavor 170 | ``` 171 | 172 | 缩进被作为空白对待而被忽略。 173 | 174 | 多次定义同一个键是不行的。 175 | 176 | ``` 177 | # 不要这样做 178 | name = "汤姆" 179 | name = "Pradyun" 180 | ``` 181 | 182 | 注意裸键和引号键是等价的: 183 | 184 | ``` 185 | # 这是不可行的 186 | spelling = "favorite" 187 | "spelling" = "favourite" 188 | ``` 189 | 190 | 只要一个键还没有被直接定义过,你就仍可以对它和它下属的键名赋值。 191 | 192 | ``` 193 | # 这使“fruit”键作为表存在。 194 | fruit.apple.smooth = true 195 | 196 | # 所以接下来你可以像中这样对“fruit”表添加内容: 197 | fruit.orange = 2 198 | ``` 199 | 200 | ``` 201 | # 以下是错误的 202 | 203 | # 这将 fruit.apple 的值定义为一个整数。 204 | fruit.apple = 1 205 | 206 | # 但接下来这将 fruit.apple 像表一样对待了。 207 | # 整数不能变成表。 208 | fruit.apple.smooth = true 209 | ``` 210 | 211 | 不鼓励跳跃式地定义点分隔键。 212 | 213 | ```toml 214 | # 有效但不鼓励 215 | 216 | apple.type = "水果" 217 | orange.type = "水果" 218 | 219 | apple.skin = "薄" 220 | orange.skin = "厚" 221 | 222 | apple.color = "红" 223 | orange.color = "橙" 224 | ``` 225 | 226 | ```toml 227 | # 建议 228 | 229 | apple.type = "水果" 230 | apple.skin = "薄" 231 | apple.color = "红" 232 | 233 | orange.type = "水果" 234 | orange.skin = "厚" 235 | orange.color = "红" 236 | ``` 237 | 238 | 由于裸键可以仅由 ASCII 整数组成,所以可能写出看起来像浮点数、但实际上是两部分的点分隔键。 239 | 除非你有充分的理由(基本不太会),否则不要这样做。 240 | 241 | ```toml 242 | 3.14159 = "派" 243 | ``` 244 | 245 | 上面的 TOML 对应以下 JSON。 246 | 247 | ```json 248 | { "3": { "14159": "派" } } 249 | ``` 250 | 251 | [字符串](#user-content-string)  252 | -------- 253 | 254 | 共有四种方式来表示字符串:基础式的,多行基础式的,字面量式的,和多行字面量式的。 255 | 所有字符串都只能包含有效的 UTF-8 字符。 256 | 257 | **基础字符串**由引号(`"`)包裹。 258 | 任何 Unicode 字符都可以使用,除了那些必须转义的:引号,反斜杠,以及除 Tab 外的控制字符(U+0000 至 U+0008,U+000A 至 U+001F,U+007F)。 259 | 260 | ```toml 261 | str = "我是一个字符串。\"你可以把我引起来\"。姓名\tJos\u00E9\n位置\t旧金山。" 262 | ``` 263 | 264 | 为了方便,一些流行的字符有其简便转义写法。 265 | 266 | ``` 267 | \b - backspace (U+0008) 268 | \t - tab (U+0009) 269 | \n - linefeed (U+000A) 270 | \f - form feed (U+000C) 271 | \r - carriage return (U+000D) 272 | \" - quote (U+0022) 273 | \\ - backslash (U+005C) 274 | \uXXXX - unicode (U+XXXX) 275 | \UXXXXXXXX - unicode (U+XXXXXXXX) 276 | ``` 277 | 278 | 任何 Unicode 字符都可以用 `\uXXXX` 或 `\UXXXXXXXX` 的形式来转义。 279 | 转义码必须是有效的 Unicode [标量值](https://unicode.org/glossary/#unicode_scalar_value)。 280 | 281 | 所有上面未列出的其它转义序列都是保留的;如果用了,TOML 应当产生错误。 282 | 283 | 有时你需要表示一小篇文本(例如译文)或者想要对非常长的字符串进行折行。 284 | TOML 对此进行了简化。 285 | 286 | **多行基础字符串**由三个引号包裹,允许折行。 287 | 紧随开头引号的那个换行会被去除。 288 | 其它空白和换行会被原样保留。 289 | 290 | ```toml 291 | str1 = """ 292 | 玫瑰是红色的 293 | 紫罗兰是蓝色的""" 294 | ``` 295 | 296 | TOML 解析器可以相对灵活地解析成对所在平台有效的换行字符。 297 | 298 | ```toml 299 | # 在 Unix 系统,上面的多行字符串可能等同于: 300 | str2 = "玫瑰是红色的\n紫罗兰是蓝色的" 301 | 302 | # 在 Windows 系统,它可能等价于: 303 | str3 = "玫瑰是红色的\r\n紫罗兰是蓝色的" 304 | ``` 305 | 306 | 想书写长字符串却不想引入无关空白,可以用“行末反斜杠”。 307 | 当一行的最后一个非空白字符是未被转义的 `\` 时,它会连同它后面的所有空白(包括换行)一起被去除,直到下一个非空白字符或结束引号为止。 308 | 所有对基础字符串有效的转义序列,对多行基础字符串也同样适用。 309 | 310 | ```toml 311 | # 下列字符串的每一个字节都完全相同: 312 | str1 = "那只敏捷的棕狐狸跳过了那只懒狗。" 313 | 314 | str2 = """ 315 | 那只敏捷的棕\ 316 | 317 | 318 | 狐狸跳过了\ 319 | 那只懒狗。""" 320 | 321 | str3 = """\ 322 | 那只敏捷的棕\ 323 | 狐狸跳过了\ 324 | 那只懒狗。\ 325 | """ 326 | ``` 327 | 328 | 任何 Unicode 字符都可以使用,除了那些必须被转义的:反斜杠和除 Tab、换行符、回车符外的控制字符(U+0000 至 U+0008,U+000B,U+000C,U+000E 至 U+001F,U+007F)。 329 | 回车符(U+000D)仅被允许作为换行序列中的一部分。 330 | 331 | 你可以在多行基础字符串内的任何地方写一个引号或两个毗连的引号。 332 | 它们也可以写在紧邻界分符内的位置。 333 | 334 | ```toml 335 | str4 = """这有两个引号:""。够简单。""" 336 | # str5 = """这有两个引号:"""。""" # 错误 337 | str5 = """这有三个引号:""\"。""" 338 | str6 = """这有十五个引号:""\"""\"""\"""\"""\"。""" 339 | 340 | # "这,"她说,"只是个无意义的条款。" 341 | str7 = """"这,"她说,"只是个无意义的条款。"""" 342 | ``` 343 | 344 | 如果你常常要指定 Windows 路径或正则表达式,那么必须转义反斜杠就马上成为啰嗦而易错的了。 345 | 为了帮助搞定这点,TOML 支持字面量字符串,它完全不允许转义。 346 | 347 | **字面量字符串**由单引号包裹。 348 | 类似于基础字符串,他们只能表现为单行: 349 | 350 | ```toml 351 | # 所见即所得。 352 | winpath = 'C:\Users\nodejs\templates' 353 | winpath2 = '\\ServerX\admin$\system32\' 354 | quoted = '汤姆·"达布斯"·普雷斯顿—维尔纳' 355 | regex = '<\i\c*\s*>' 356 | ``` 357 | 358 | 由于没有转义,无法在由单引号包裹的字面量字符串中写入单引号。 359 | 万幸,TOML 支持一种多行版本的字面量字符串来解决这个问题。 360 | 361 | **多行字面量字符串**两侧各有三个单引号来包裹,允许换行。 362 | 类似于字面量字符串,无论任何转义都不存在。 363 | 紧随开始标记的那个换行会被剔除。 364 | 开始结束标记之间的所有其它内容会原样对待。 365 | 366 | ```toml 367 | regex2 = '''I [dw]on't need \d{2} apples''' 368 | lines = ''' 369 | 原始字符串中的 370 | 第一个换行被剔除了。 371 | 所有其它空白 372 | 都保留了。 373 | ''' 374 | ``` 375 | 376 | 你可以在多行字面量字符串中的任何位置写一个或两个单引号,但三个以上的单引号序列不可以。 377 | 378 | ```toml 379 | quot15 = '''这有十五个引号:"""""""""""""""''' 380 | 381 | # apos15 = '''这有十五个撇号:'''''''''''''''''' # 错误 382 | apos15 = "这有十五个撇号:'''''''''''''''" 383 | 384 | # '那,'她说,'仍然没有意义。' 385 | str = ''''那,'她说,'仍然没有意义。'''' 386 | ``` 387 | 388 | 除 Tab 以外的所有控制字符都不允许出现在字面量字符串中。 389 | 因此,对于二进制数据,建议你使用 Base64 或其它合适的 ASCII 或 UTF-8 编码。 390 | 对那些编码的处理方式,将交由应用程序自己来确定。 391 | 392 | [整数](#user-content-integer)  393 | ------ 394 | 395 | 整数是纯数字。 396 | 正数可以有加号前缀。 397 | 负数的前缀是减号。 398 | 399 | ```toml 400 | int1 = +99 401 | int2 = 42 402 | int3 = 0 403 | int4 = -17 404 | ``` 405 | 406 | 对于大数,你可以在数字之间用下划线来增强可读性。 407 | 每个下划线两侧必须至少有一个数字。 408 | 409 | ```toml 410 | int5 = 1_000 411 | int6 = 5_349_221 412 | int7 = 53_49_221 # 印度记数体系分组 413 | int8 = 1_2_3_4_5 # 无误但不鼓励 414 | ``` 415 | 416 | 前导零是不允许的。 417 | 整数值 `-0` 与 `+0` 是有效的,并等同于无前缀的零。 418 | 419 | 非负整数值也可以用十六进制、八进制或二进制来表示。 420 | 在这些格式中,`+` 不被允许,而(前缀后的)前导零是允许的。 421 | 十六进制值大小写不敏感。 422 | 数字间的下划线是允许的(但不能存在于前缀和值之间)。 423 | 424 | ```toml 425 | # 带有 `0x` 前缀的十六进制 426 | hex1 = 0xDEADBEEF 427 | hex2 = 0xdeadbeef 428 | hex3 = 0xdead_beef 429 | 430 | # 带有 `0o` 前缀的八进制 431 | oct1 = 0o01234567 432 | oct2 = 0o755 # 对于表示 Unix 文件权限很有用 433 | 434 | # 带有 `0b` 前缀的二进制 435 | bin1 = 0b11010110 436 | ``` 437 | 438 | 任何 64 位有符号整数(从 −2^63 到 2^63−1)都应当被接受并无损处理。 439 | 如果无法无损表现某个整数,则必须抛出错误。 440 | 441 | [浮点数](#user-content-float)  442 | -------- 443 | 444 | 浮点数应当被实现为 IEEE 754 binary64 值。 445 | 446 | 一个浮点数由一个整数部分(遵从与十进制整数值相同的规则)后跟上一个小数部分和/或一个指数部分组成。 447 | 如果小数部分和指数部分兼有,那小数部分必须在指数部分前面。 448 | 449 | ```toml 450 | # 小数 451 | flt1 = +1.0 452 | flt2 = 3.1415 453 | flt3 = -0.01 454 | 455 | # 指数 456 | flt4 = 5e+22 457 | flt5 = 1e06 458 | flt6 = -2E-2 459 | 460 | # 都有 461 | flt7 = 6.626e-34 462 | ``` 463 | 464 | 小数部分是一个小数点后跟一个或多个数字。 465 | 466 | 一个指数部分是一个 E(大小写均可)后跟一个整数部分(遵从与十进制整数值相同的规则,但可以包含前导零)。 467 | 468 | 小数点,如果有用到的话,每侧必须紧邻至少一个数字。 469 | 470 | ``` 471 | # 非法的浮点数 472 | invalid_float_1 = .7 473 | invalid_float_2 = 7. 474 | invalid_float_3 = 3.e+20 475 | ``` 476 | 477 | 与整数相似,你可以使用下划线来增强可读性。 478 | 每个下划线必须被至少一个数字围绕。 479 | 480 | ```toml 481 | flt8 = 224_617.445_991_228 482 | ``` 483 | 484 | 浮点数值 `-0.0` 与 `+0.0` 是有效的,并且应当遵从 IEEE 754。 485 | 486 | 特殊浮点值也能够表示。 487 | 它们是小写的。 488 | 489 | ```toml 490 | # 无穷 491 | sf1 = inf # 正无穷 492 | sf2 = +inf # 正无穷 493 | sf3 = -inf # 负无穷 494 | 495 | # 非数 496 | sf4 = nan # 实际上对应信号非数码还是静默非数码,取决于实现 497 | sf5 = +nan # 等同于 `nan` 498 | sf6 = -nan # 正确,实际码取决于实现 499 | ``` 500 | 501 | [布尔值](#user-content-boolean)  502 | -------- 503 | 504 | 布尔值就是你所惯用的那样。 505 | 要小写。 506 | 507 | ```toml 508 | bool1 = true 509 | bool2 = false 510 | ``` 511 | 512 | [坐标日期时刻](#user-content-offset-date-time)  513 | -------------- 514 | 515 | 要明确无误地表示世上的一个特定时间,你可以使用指定了时区偏移量的 [RFC 3339](https://tools.ietf.org/html/rfc3339) 格式的日期时刻。 516 | 517 | ```toml 518 | odt1 = 1979-05-27T07:32:00Z 519 | odt2 = 1979-05-27T00:32:00-07:00 520 | odt3 = 1979-05-27T00:32:00.999999-07:00 521 | ``` 522 | 523 | 出于可读性的目的,你可以用一个空格字符替代日期和时刻之间的 T(RFC 3339 的第 5.6 节中允许了这样做)。 524 | 525 | ```toml 526 | odt4 = 1979-05-27 07:32:00Z 527 | ``` 528 | 529 | 毫秒级的精度是必须的。 530 | 更高精度的小数秒取决于实现。 531 | 如果它的值超出了实现所支持的精度,那多余的部分必须被舍弃,而不能四舍五入。 532 | 533 | [各地日期时刻](#user-content-local-date-time)  534 | -------------- 535 | 536 | 如果你省略了 [RFC 3339](https://tools.ietf.org/html/rfc3339) 日期时刻中的时区偏移量,这表示该日期时刻的使用并不涉及时区偏移。 537 | 在没有其它信息的情况下,并不知道它究竟该被转化成世上的哪一刻。 538 | 如果仍被要求转化,那结果将取决于实现。 539 | 540 | ```toml 541 | ldt1 = 1979-05-27T07:32:00 542 | ldt2 = 1979-05-27T00:32:00.999999 543 | ``` 544 | 545 | 毫秒级的精度是必须的。 546 | 更高精度的小数秒取决于实现。 547 | 如果它的值超出了实现所支持的精度,那多余的部分必须被舍弃,而不能四舍五入。 548 | 549 | [各地日期](#user-content-local-date)  550 | ---------- 551 | 552 | 如果你只写了 [RFC 3339](https://tools.ietf.org/html/rfc3339) 日期时刻中的日期部分,那它表示一整天,同时也不涉及时区偏移。 553 | 554 | ```toml 555 | ld1 = 1979-05-27 556 | ``` 557 | 558 | [各地时刻](#user-content-local-time)  559 | ---------- 560 | 561 | 如果你只写了 [RFC 3339](https://tools.ietf.org/html/rfc3339) 日期时刻中的时刻部分,它将只表示一天之中的那个时刻,而与任何特定的日期无关、亦不涉及时区偏移。 562 | 563 | ```toml 564 | lt1 = 07:32:00 565 | lt2 = 00:32:00.999999 566 | ``` 567 | 568 | 毫秒级的精度是必须的。 569 | 更高精度的小数秒取决于实现。 570 | 如果它的值超出了实现所支持的精度,那多余的部分必须被舍弃,而不能四舍五入。 571 | 572 | [数组](#user-content-array)  573 | ------ 574 | 575 | 数组是内含值的方括号。 576 | 空白会被忽略。 577 | 子元素由逗号分隔。 578 | 数组可以包含与键值对所允许的相同数据类型的值。 579 | 可以混合不同类型的值。 580 | 581 | ```toml 582 | integers = [ 1, 2, 3 ] 583 | colors = [ "红", "黄", "绿" ] 584 | nested_array_of_ints = [ [ 1, 2 ], [3, 4, 5] ] 585 | nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] 586 | string_array = [ "所有的", '字符串', """是相同的""", '''类型''' ] 587 | 588 | # 允许混合类型的数组 589 | numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] 590 | contributors = [ 591 | "Foo Bar ", 592 | { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } 593 | ] 594 | ``` 595 | 596 | 数组可以跨行。 597 | 数组的最后一个值后面可以有终逗号(也称为尾逗号)。 598 | 值、逗号、结束括号前可以存在任意数量的换行和注释。 599 | 数组值和逗号之间的缩进被作为空白对待而被忽略。 600 | 601 | ```toml 602 | integers2 = [ 603 | 1, 2, 3 604 | ] 605 | 606 | integers3 = [ 607 | 1, 608 | 2, # 这是可以的 609 | ] 610 | ``` 611 | 612 | [表](#user-content-table)  613 | ---- 614 | 615 | 表(也被称为哈希表或字典)是键值对的集合。 616 | 它们由表头定义,连同方括号作为单独的行出现。 617 | 看得出表头不同于数组,因为数组只有值。 618 | 619 | ```toml 620 | [table] 621 | ``` 622 | 623 | 在它下方,直至下一个表头或文件结束,都是这个表的键值对。 624 | 表不保证保持键值对的指定顺序。 625 | 626 | ```toml 627 | [table-1] 628 | key1 = "some string" 629 | key2 = 123 630 | 631 | [table-2] 632 | key1 = "another string" 633 | key2 = 456 634 | ``` 635 | 636 | 表名的规则与键名相同(见前文[键名](#user-content-keys)定义)。 637 | 638 | ```toml 639 | [dog."tater.man"] 640 | type.name = "哈巴狗" 641 | ``` 642 | 643 | 这在 JSON 那儿,是以下结构: 644 | 645 | ```json 646 | { "dog": { "tater.man": { "type": { "name": "哈巴狗" } } } } 647 | ``` 648 | 649 | 键名周围的空格会被忽略。 650 | 然而,最佳实践还是不要有任何多余的空白。 651 | 652 | ```toml 653 | [a.b.c] # 这是最佳实践 654 | [ d.e.f ] # 等同于 [d.e.f] 655 | [ g . h . i ] # 等同于 [g.h.i] 656 | [ j . "ʞ" . 'l' ] # 等同于 [j."ʞ".'l'] 657 | ``` 658 | 659 | 缩进被作为空白对待而被忽略。 660 | 661 | 你不必层层完整地写出你不想写的所有途径的父表。 662 | TOML 知道该怎么办。 663 | 664 | ```toml 665 | # [x] 你 666 | # [x.y] 不 667 | # [x.y.z] 需要这些 668 | [x.y.z.w] # 来让这生效 669 | 670 | [x] # 后置父表定义是可以的 671 | ``` 672 | 673 | 空表是允许的,只要里面没有键值对就行了。 674 | 675 | 类似于键名,你不能重复定义一个表。 676 | 这样做是错误的。 677 | 678 | ``` 679 | # 不要这样做 680 | 681 | [fruit] 682 | apple = "红色" 683 | 684 | [fruit] 685 | orange = "橙色" 686 | ``` 687 | 688 | ``` 689 | # 也不要这样做 690 | 691 | [fruit] 692 | apple = "红色" 693 | 694 | [fruit.apple] 695 | texture = "光滑" 696 | ``` 697 | 698 | 不鼓励无序地定义表。 699 | 700 | ```toml 701 | # 正确但不鼓励 702 | [fruit.apple] 703 | [animal] 704 | [fruit.orange] 705 | ``` 706 | 707 | ```toml 708 | # 推荐 709 | [fruit.apple] 710 | [fruit.orange] 711 | [animal] 712 | ``` 713 | 714 | 顶层表,又被称为根表,于文档开始处开始并在第一个表头(或文件结束处)前结束。 715 | 不同于其它表,它没有名字且无法后置。 716 | 717 | ```toml 718 | # 顶层表开始。 719 | name = "Fido" 720 | breed = "哈巴狗" 721 | 722 | # 顶层表结束。 723 | [owner] 724 | name = "Regina Dogman" 725 | member_since = 1999-08-04 726 | ``` 727 | 728 | 点分隔键为最后一个键名前的每个键名创建并定义一个表。 729 | 这样的表必须将它所有的键值对定义在当前 `[table]` 头下,或在根表中(如果在所有头前被定义),或在行内表中。 730 | 731 | ```toml 732 | fruit.apple.color = "红色" 733 | # 定义一个名为 fruit 的表 734 | # 定义一个名为 fruit.apple 的表 735 | 736 | fruit.apple.taste.sweet = true 737 | # 定义一个名为 fruit.apple.taste 的表 738 | # fruit 和 fruit.apple 已经创建过了 739 | ``` 740 | 741 | 由于表不能定义多于一次,不允许使用 `[table]` 头重定义这样的表。 742 | 同样地,使用点分隔键来重定义已经以 `[table]` 形式定义过的表也是不允许的。 743 | 不过,`[table]` 形式可以被用来定义通过点分隔键定义的表中的子表。 744 | 745 | ```toml 746 | [fruit] 747 | apple.color = "红色" 748 | apple.taste.sweet = true 749 | 750 | # [fruit.apple] # 错误 751 | # [fruit.apple.taste] # 错误 752 | 753 | [fruit.apple.texture] # 你可以添加子表 754 | smooth = true 755 | ``` 756 | 757 | [行内表](#user-content-inline-table)  758 | -------- 759 | 760 | 行内表提供了一种更为紧凑的语法来表示表。 761 | 对于否则就很啰嗦的成组数据,这尤其有用。 762 | 行内表被完整地定义在花括号之中:`{` 和 `}`。 763 | 括号中,可以出现零或更多个以逗号分隔的键值对。 764 | 键值对采取与标准表中的键值对相同的形式。 765 | 什么类型的值都可以,包括行内表。 766 | 767 | 行内表得出现在同一行内。 768 | 行内表中,最后一对键值对后不允许终逗号(也称为尾逗号)。 769 | 不允许花括号中出现任何换行,除非在值中它们有效。 770 | 即便如此,也强烈不建议把一个行内表搞成纵跨多行的样子。 771 | 如果你发现自己真的需要,那意味着你应该使用标准表。 772 | 773 | ```toml 774 | name = { first = "汤姆", last = "普雷斯顿—维尔纳" } 775 | point = { x = 1, y = 2 } 776 | animal = { type.name = "哈巴狗" } 777 | ``` 778 | 779 | 上述行内表等同于下面的标准表定义: 780 | 781 | ```toml 782 | [name] 783 | first = "汤姆" 784 | last = "普雷斯顿—维尔纳" 785 | 786 | [point] 787 | x = 1 788 | y = 2 789 | 790 | [animal] 791 | type.name = "哈巴狗" 792 | ``` 793 | 794 | 行内表是独立自足的,在内部定义全部的键与子表。 795 | 不能在括号以外的地方,再添加键与子表。 796 | 797 | ```toml 798 | [product] 799 | type = { name = "钉子" } 800 | # type.edible = false # 错误 801 | ``` 802 | 803 | 类似地,行内表不能被用来向一个已定义的表添加键或子表。 804 | 805 | ```toml 806 | [product] 807 | type.name = "钉子" 808 | # type = { edible = false } # 错误 809 | ``` 810 | 811 | [表数组](#user-content-array-of-tables)  812 | -------- 813 | 814 | 最后一个还没讲到的语法允许你写表数组。 815 | 这可以通过把表名写在双方括号里的表头来表示。 816 | 表头的第一例定义了这个数组及其首个表元素,而后续的每个则在该数组中创建并定义一个新的表元素。 817 | 这些表按出现顺序插入该数组。 818 | 819 | ```toml 820 | [[products]] 821 | name = "锤子" 822 | sku = 738594937 823 | 824 | [[products]] # 数组里的空表 825 | 826 | [[products]] 827 | name = "钉子" 828 | sku = 284758393 829 | 830 | color = "灰色" 831 | ``` 832 | 833 | 这在 JSON 那儿,是以下结构。 834 | 835 | ```json 836 | { 837 | "products": [ 838 | { "name": "锤子", "sku": 738594937 }, 839 | { }, 840 | { "name": "钉子", "sku": 284758393, "color": "gray" } 841 | ] 842 | } 843 | ``` 844 | 845 | 任何对表数组的引用都指向该数组里最近定义的表元素。 846 | 这允许你在最近的表内定义子表,甚至子表数组。 847 | 848 | ```toml 849 | [[fruits]] 850 | name = "苹果" 851 | 852 | [fruits.physical] # 子表 853 | color = "红色" 854 | shape = "圆形" 855 | 856 | [[fruits.varieties]] # 嵌套表数组 857 | name = "蛇果" 858 | 859 | [[fruits.varieties]] 860 | name = "澳洲青苹" 861 | 862 | [[fruits]] 863 | name = "香蕉" 864 | 865 | [[fruits.varieties]] 866 | name = "车前草" 867 | ``` 868 | 869 | 上述 TOML 对应下面的 JSON。 870 | 871 | ```json 872 | { 873 | "fruits": [ 874 | { 875 | "name": "苹果", 876 | "physical": { 877 | "color": "红色", 878 | "shape": "圆形" 879 | }, 880 | "varieties": [ 881 | { "name": "蛇果" }, 882 | { "name": "澳洲青苹" } 883 | ] 884 | }, 885 | { 886 | "name": "香蕉", 887 | "varieties": [ 888 | { "name": "车前草" } 889 | ] 890 | } 891 | ] 892 | } 893 | ``` 894 | 895 | 如果一个表或表数组的父级是一个数组元素,该元素必须在定义子级前先定义。 896 | 顺序颠倒的行为,必须在解析时报错。 897 | 898 | ``` 899 | # 错误的 TOML 文档 900 | [fruit.physical] # 子表,但它应该隶属于哪个父元素? 901 | color = "红色" 902 | shape = "圆形" 903 | 904 | [[fruit]] # 解析器必须在发现“fruit”是数组而非表时抛出错误 905 | name = "苹果" 906 | ``` 907 | 908 | 若试图向一个静态定义的数组追加内容,即便数组尚且为空,也必须在解析时报错。 909 | 910 | ``` 911 | # 错误的 TOML 文档 912 | fruits = [] 913 | 914 | [[fruits]] # 不允许 915 | ``` 916 | 917 | 若试图用已经确定为数组的名称定义表,必须在解析时报错。 918 | 将数组重定义为普通表的行为,也必须在解析时报错。 919 | 920 | ``` 921 | # 错误的 TOML 文档 922 | [[fruits]] 923 | name = "苹果" 924 | 925 | [[fruits.varieties]] 926 | name = "蛇果" 927 | 928 | # 错误:该表与之前的表数组相冲突 929 | [fruits.varieties] 930 | name = "澳洲青苹" 931 | 932 | [fruits.physical] 933 | color = "红色" 934 | shape = "圆形" 935 | 936 | # 错误:该表数组与之前的表相冲突 937 | [[fruits.physical]] 938 | color = "绿色" 939 | ``` 940 | 941 | 你也可以适当使用行内表: 942 | 943 | ```toml 944 | points = [ { x = 1, y = 2, z = 3 }, 945 | { x = 7, y = 8, z = 9 }, 946 | { x = 2, y = 4, z = 8 } ] 947 | ``` 948 | 949 | [文件扩展名](#user-content-filename-extension)  950 | ------------ 951 | 952 | TOML 文件应当使用 `.toml` 扩展名。 953 | 954 | [MIME 类型](#user-content-mime-type)  955 | ----------- 956 | 957 | 在互联网上传输 TOML 文件时,恰当的 MIME 类型是 `application/toml`。 958 | 959 | [ABNF 语法](#user-content-abnf-grammar)  960 | ----------- 961 | 962 | TOML 语法的严谨说明,由一个 [ABNF 文件][abnf]另行提供。 963 | 964 | [abnf]: https://github.com/LongTengDao/TOML/blob/%E9%BE%99%E8%85%BE%E9%81%93-%E8%AF%91/toml.abnf 965 | --------------------------------------------------------------------------------