在toml以 # 作为注释,文件内容也是需要有一个 section 其下内容以 key = value 书写,key不能为空,但是可以是空引号,key可以用双引号也可以不用,value是字符串时需要用双引号引起来
不能多次定义同一个key
如果定义来一个值为 mysql.name = “name” 那么 就不能在定义 mysql = “xx”
toml的语法可以去官网看看 https://toml.io/cn/
[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格式的配置文件几乎一模一样。
但是在TOML中,数据类型非常丰富:
TOML 认为配置文件都应当支持注释。
# 这是一条 TOML 注释
# 这是一个多行的
# TOML 注释
如果你是从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
# 整数
int1 = +99
int2 = 42
int3 = 0
int4 = -17
# 十六进制带有前缀 `0x`
hex1 = 0xDEADBEEF
hex2 = 0xdeadbeef
hex3 = 0xdead_beef
# 八进制带有前缀 `0o`
oct1 = 0o01234567
oct2 = 0o755
# 二进制带有前缀 `0b`
bin1 = 0b11010110
# 小数
float1 = +1.0
float2 = 3.1415
float3 = -0.01
# 指数
float4 = 5e+22
float5 = 1e06
float6 = -2E-2
# both
float7 = 6.626e-34
# 分隔符
float8 = 224_617.445_991_228
# 无穷
infinite1 = inf # 正无穷
infinite2 = +inf # 正无穷
infinite3 = -inf # 负无穷
# 非数
not1 = nan
not2 = +nan
not3 = -nan
toml除了支持10进制表示,还支持二进制(0b)、八进制(0o)、十六进制(0x)
age1 = 0b11
age2 = 0o12
age3 = 0xb
上面的解析结果为:
{
'age1': 3,
'age2': 10,
'age3': 11
}
bool1 = true
bool2 = false
时间类型是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)
}
TOML 支持日期、时刻、日期时刻,带或者不带时区偏移。
# 坐标日期时刻
odt1 = 1979-05-27T07:32:00Z
odt2 = 1979-05-27T00:32:00-07:00
odt3 = 1979-05-27T00:32:00.999999-07:00
# 各地日期时刻
ldt1 = 1979-05-27T07:32:00
ldt2 = 1979-05-27T00:32:00.999999
# 各地日期
ld1 = 1979-05-27
# 各地时刻
lt1 = 07:32:00
lt2 = 00:32:00.999999
数组可以是同类型数据,可以换行,也可以嵌套,和python当中列表简直一模一样。
但是要注意,尽量保持元素是同类型的数据。
users = [
['iswy', '男'],
['you', '女'],
]
得到的结果:
{
'users': [
['iswy', '男'],
['you', '女']
]
}
表有点类似于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'}
}
]
}
TOML 文件应当使用 .toml
扩展名。
在互联网上传输 TOML 文件时,恰当的 MIME 类型是 application/toml
。
1.toml
os="windows"
version="10"
[server]
addr="192.168.1.1"
port="80"
http="https"
route="/test/indes"
这个有两种版本
版本1是可以直接读取文件,而且不用新建结构体
版本2已经不允许直接读取文件了
版本1
$ go get github.com/pelletier/go-toml
版本2
$ go get github.com/pelletier/go-toml/v2
反序列化
版本1直接读取文件
可以不用结构体,直接读取
package main
import (
"fmt"
"github.com/pelletier/go-toml"
)
func main() {
config, _ := toml.LoadFile("1.toml") //加载toml文件
key := config.Get("os") //读取一级key
fmt.Println(key)
/*
windows
*/
key = config.Get("server") //读取复合的一级
fmt.Println(key)
/*
addr = "192.168.1.1"
http = "https"
port = "80"
route = "/test/indes"
*/
key = config.Get("server.addr") //读取二级key
fmt.Println(key)
/*
192.168.1.1
*/
key = config.Get("abc") //读取不存在的key不会报错
fmt.Println(key)
/*
<nil>
*/
}
版本1和版本2都可以使用结构体解组
package main
import (
"fmt"
"os"
"github.com/pelletier/go-toml"
)
type Conf struct {
Os string `toml:"os"`
Version string `toml:"version"`
Server `toml:"server"`
}
type Server struct {
Addr string `toml:"addr"`
Port string `toml:"port"`
Http string `toml:"http"`
Route string `toml:"route"`
}
func main() {
var conf Conf
//打开文件
file, err := os.Open("1.toml")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
//读取文件并解组到结构体
err = toml.NewDecoder(file).Decode(&conf)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v\n", conf)
}
/*
{Os:windows Version:10 Server:{Addr:192.168.1.1 Port:80 Http:https Route:/test/indes}}
*/
如果是读取字符串
package main
import (
"fmt"
"os"
"github.com/pelletier/go-toml/v2"
)
func main() {
doc := `
version = 2
name = "go-toml"
tags = ["go", "toml"]
`
type MyConfig struct {
Version int
Name string
Tags []string
}
var cfg MyConfig
//反序列化
err = toml.Unmarshal([]byte(doc), &cfg)
if err != nil {
panic(err)
}
fmt.Println("version:", cfg.Version)
fmt.Println("name:", cfg.Name)
fmt.Println("tags:", cfg.Tags)
/*
version: 2
name: go-toml
tags: [go toml]
*/
}
序列化
package main
import (
"fmt"
"github.com/pelletier/go-toml"
)
func main() {
type MyConfig struct {
Version int
Name string
Tags []string
}
cfg := MyConfig{
Version: 2,
Name: "go-toml",
Tags: []string{"go", "toml"},
}
b, err := toml.Marshal(cfg)
if err != nil {
panic(err)
}
fmt.Println(string(b))
/*
Name = "go-toml"
Tags = ["go", "toml"]
Version = 2
*/
}
Go-toml 提供了三个方便的命令行工具:
tomljson
:读取 TOML 文件并输出其 JSON 表示形式。
$ go install github.com/pelletier/go-toml/v2/cmd/tomljson@latest
$ tomljson --help
jsontoml
:读取 JSON 文件并输出 TOML 表示形式。
$ go install github.com/pelletier/go-toml/v2/cmd/jsontoml@latest
$ jsontoml --help
tomll
:检查并重新格式化 TOML 文件。
$ go install github.com/pelletier/go-toml/v2/cmd/tomll@latest
$ tomll --help
$ go get github.com/BurntSushi/toml
带有一个TOML验证器CLI工具
$ go install github.com/BurntSushi/toml/cmd/tomlv@latest
$ tomlv some-toml-file.toml
package main
import (
"fmt"
"os"
"github.com/BurntSushi/toml"
)
type Conf struct {
Os string `toml:"os"`
Version string `toml:"version"`
Server `toml:"server"`
}
type Server struct {
Addr string `toml:"addr"`
Port string `toml:"port"`
Http string `toml:"http"`
Route string `toml:"route"`
}
func main() {
var conf Conf
file, err := os.Open("1.toml")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
//读取文件
_, err = toml.DecodeFile("1.toml", &conf)
fmt.Printf("%+v\n", conf)
/*
{Os:windows Version:10 Server:{Addr:192.168.1.1 Port:80 Http:https Route:/test/indes}}
*/
if err != nil {
fmt.Println(err)
return
}
}
package main
import (
"fmt"
"time"
"github.com/BurntSushi/toml"
)
func main() {
tomlData := `Age = 25
Cats = [ "Cauchy", "Plato" ]
Pi = 3.14
Perfection = [ 6, 28, 496, 8128 ]
DOB = 1987-07-05T05:45:00Z
`
type Config1 struct {
Age int
Cats []string
Pi float64
Perfection []int
DOB time.Time
}
var conf1 Config1
_, err = toml.Decode(tomlData, &conf1)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v\n", conf1)
//或者使用Unmarshal
var conf2 Config1
err = toml.Unmarshal([]byte(tomlData), &conf2)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v\n", conf1)
}