您的位置:首页 > 技术中心 > 其他 >

怎么用Python解析toml配置文件

时间:2023-05-21 22:26

举个例子

有了 ini 和 yaml,相信 toml 学习来也很简单,先直接看一个例子吧。

import tomlconfig = """title = "toml 小栗子"[owner]name = "古明地觉"age = 17place = "东方地灵殿"nickname = ["小五", "少女觉", "觉大人"][database]host = "127.0.0.1"port = 5432username = "satori"password = "123456"echo = true[server]    [server.v1]    api = "1.1"    enable = false        [server.v2]    api = "1.2"    enable = true[client]client = [    ["socket", "webservice"],     [5555]]address = [    "xxxx",    "yyyy"]"""# loads:从字符串加载# load:从文件加载# dumps:生成 toml 格式字符串# dump:生成 toml 格式字符串并写入文件中data = toml.loads(config)print(data)"""{    'title': 'toml 小栗子',     'owner': {'name': '古明地觉',               'age': 17,               'place': '东方地灵殿',               'nickname': ['小五', '少女觉', '觉大人']},    'database': {'host': '127.0.0.1',                  'port': 5432,                 'username': 'satori',                  'password': '123456',                  'echo': True},    'server': {'v1': {'api': '1.1', 'enable': False},                'v2': {'api': '1.2', 'enable': True}},    'client': {'client': [['socket', 'webservice'], [5555]],                'address': ['xxxx', 'yyyy']}}"""

toml 是采用 var = value 的形式进行配置,然后也有类似于 ini 里面的 section,每个 section 都是字典中的一个 key,然后该 key 也对应一个字典。由于缺乏section,我们需要注意到最初的标题,因此它是一个独立的键。

而且还有一点就是 toml 支持嵌套,我们看到 server.v1,表示 v1 是 server 对应的字典里面的一个 key,然后 v1 对应的值还是一个字典。

toml 变得更加简单了,而且写来也非常像 Python,它有如下特点:

toml 文件是大小写敏感的;

toml 文件必须是有效的 UTF-8 编码的 Unicode 文档;

toml 文件的空白符应该是 Tab 或者空格;

toml 文件的换行是 LF 或者 CRLF;

然后我们来介绍一下 toml 的数据结构。

注释

toml 采用 # 表示注释,举个例子:

# 这是注释key = "value"  # 也是注释

可以解析一下看看会得到什么,剧透:会得到只包含一个键值对的字典。

键值对

基本构成区块为键值对,在等号左侧为键名,在右侧为值,同时忽略键名和键值周围的空白的 TOML 文档。此外键、等号和值必须在同一行(不过有些值可以跨多行)。

key = "value"

键名可以以裸键、引号键或点分隔键的形式出现。裸键只允许包含ASCII字符、数字、下划线和连字符。

import tomlconfig = """key = "value"bare_key = "value"bare-key = "value"# 1234 会被当成字符串1234 = "value"  """data = toml.loads(config)print(data)"""{'key': 'value',  'bare_key': 'value',  'bare-key': 'value',  '1234': 'value'}"""

如果不是裸键,那么就必须使用引号括起来,但是此时也支持我们使用更加广泛的键名,但除了特殊场景,否则使用裸键是最佳实践。

import tomlconfig = """"127.0.0.1" = "value""character encoding" = "value""ʎǝʞ" = "value"'key2' = "value"'quoted "value"' = "value" """data = toml.loads(config)print(data)"""{'127.0.0.1': 'value',  'character encoding': 'value',  'ʎǝʞ': 'value',  'key2': 'value',  'quoted "value"': 'value'}"""

注意:裸键不能为空,但空引号键是允许的(虽然不建议如此)。

= "没有键名"  # 错误"" = "空"     # 正确但不鼓励'' = '空'     # 正确但不鼓励

然后是点分隔键,它是一系列通过点相连的裸键或引号键,这允许我们将相近属性放在一起:

import tomlconfig = """name = "橙子"physical.color = "橙色"physical.shape = "圆形"site."google.com" = truesite.google.com = truea.b.c.d = 123"""data = toml.loads(config)print(data)"""{    'name': '橙子',    'physical': {'color': '橙色',                 'shape': '圆形'},    'site': {'google.com': True,             'google': {'com': True}},    'a': {'b': {'c': {'d': 123}}}}"""

我们看到这个点分隔符不错哟,自动实现了嵌套结构,并且点分隔符周围的空白会被忽略。

fruit.name = "香蕉"     # 这是最佳实践fruit. color = "黄色"    # 等同于 fruit.colorfruit . flavor = "香蕉"   # 等同于 fruit.flavor

注意:多次定义同一个键是不行的。

import tomlconfig = """# name 和 "name" 是等价的name = "古明地觉""name" = "古明地恋"  """try:    data = toml.loads(config)except toml.decoder.TomlDecodeError as e:    print(e)"""Duplicate keys! (line 4 column 1 char 36)"""

对于点分隔键也是如此,只要一个键还没有被直接定义过,我们就仍可以对它和它下属的键名赋值。

import tomlconfig = """fruit.apple.smooth = true# 此时可以继续操作 fruit、fruit.apple,它们都是字典# 给 fruit 这个字典加一个 key  fruit.orange = 2  # 给 fruit.apple 加一个 keyfruit.apple.color = "red"   """data = toml.loads(config)print(data)"""{    'fruit': {'apple': {'smooth': True,                         'color': 'red'},               'orange': 2}}"""

但下面这个操作是不行的:

# 将 fruit.apple 的值定义为一个整数fruit.apple = 1# 但接下来就不合法了,因为整数不能变成字典fruit.apple.smooth = true# 如果我们设置 fruit.apple = {},那么第二个赋值是可以的# 没错,我们可以通过 {} 直接创建一个字典

可以看到,真的很像 Python。然后再来说一个特例:

import tomlconfig = """3.14 = "pi"  "3.14" = "pi"  """data = toml.loads(config)print(data)"""{'3': {'14': 'pi'}, '3.14': 'pi'}"""

如果键是浮点数,那么需要使用引号括起来,否则会被解释为点分隔键。

看完了键,再来看看值(value),其实对于 toml 来说,值比键要简单的多得多。

字符串

字符串共有四种方式来表示:基础式的,多行基础式的,字面量式的,和多行字面量式的。

1)基础字符串由引号包裹,任何 Unicode 字符都可以使用,除了那些必须转义的。

import tomlconfig = """str = '我是一个字符串,"你可以把我引起来"' """data = toml.loads(config)print(data)"""{'str': '我是一个字符串,"你可以把我引起来"'}"""

多行字符串可以用三个引号括起来,可以包含换行。但是需要注意,开头引号之后的第一个换行会被去除,其他的空格和换行符会被保留。

import tomlconfig = """str = '''玫瑰是红色的紫罗兰是蓝色的'''"""data = toml.loads(config)print(data)"""{'str': '玫瑰是红色的
紫罗兰是蓝色的
'}"""

这里的引号可以是双引号、也可以是单引号。

整数

整数是纯数字,正数可以有加号前缀,负数的前缀是减号。

import tomlconfig = """int1 = +99int2 = 42int3 = 0int4 = -17# 对于大数,可以在数字之间用下划线来增强可读性# 每个下划线两侧必须至少有一个数字。int5 = 1_000int6 = 5_349_221int7 = 53_49_221  # 印度记数体系分组int8 = 1_2_3_4_5  # 无误但不鼓励"""data = toml.loads(config)print(data)"""{'int1': 99, 'int2': 42, 'int3': 0, 'int4': -17, 'int5': 1000, 'int6': 5349221, 'int7': 5349221, 'int8': 12345}"""

但是注意:数字不能以零开头,除了 0 本身。无前缀的零、-0和+0是等效的。非负整数值也可以用十六进制、八进制或二进制来表示。

# 带有 `0x` 前缀的十六进制,大小写均可hex1 = 0xDEADBEEFhex2 = 0xdeadbeefhex3 = 0xdead_beef# 带有 `0o` 前缀的八进制oct1 = 0o01234567oct2 = 0o755 # 对于表示 Unix 文件权限很有用# 带有 `0b` 前缀的二进制bin1 = 0b11010110

浮点数

一个浮点数可以由整数部分和小数部分组成,也可以由指数部分组成。整数部分和小数部分遵从与十进制整数值相同的规则。如果小数部分和指数部分兼有,那小数部分必须在指数部分前面。

import tomlconfig = """# 小数flt1 = +1.0flt2 = 3.1415flt3 = -0.01# 指数flt4 = 5e+22flt5 = 1e06flt6 = -2E-2flt7 = 6.626e-34"""data = toml.loads(config)print(data)"""{'flt1': 1.0, 'flt2': 3.1415, 'flt3': -0.01, 'flt4': 5e+22, 'flt5': 1000000.0, 'flt6': -0.02, 'flt7': 6.626e-34}"""

小数部分是一个小数点后跟一个或多个数字,一个指数部分是一个 E(大小写均可)后跟一个整数部分(遵从与十进制整数值相同的规则,但可以包含前导零)。小数点,如果有用到的话,每侧必须紧邻至少一个数字。

# 非法的浮点数invalid_float_1 = .7invalid_float_2 = 7.invalid_float_3 = 3.e+20

与整数相似,可以使用下划线来增强可读性,每个下划线必须被至少一个数字围绕。

flt8 = 224_617.445_991_228

浮点数值 -0.0 与 +0.0 是有效的,并且应当遵从 IEEE 754。特殊浮点值也能够表示:

# 无穷sf1 = inf  # 正无穷sf2 = +inf # 正无穷sf3 = -inf # 负无穷# 非数sf4 = nan  # 是对应信号非数码还是静默非数码,取决于实现sf5 = +nan # 等同于 `nan`sf6 = -nan # 正确,实际码取决于实现

布尔值

布尔值就是惯用的那样,但要小写。

bool1 = truebool2 = false

日期

可以是普通的 datetime,或者是遵循 ISO-8859-1 格式的日期。

import tomlconfig = """dt1 = 2020-01-01T12:33:22+00:00dt2 = 2020-11-12 12:11:33dt3 = 2020-11-23"""data = toml.loads(config)print(data)"""{'dt1': datetime.datetime(2020, 1, 1, 12, 33, 22, tzinfo=...),  'dt2': datetime.datetime(2020, 11, 12, 12, 11, 33),  'dt3': datetime.date(2020, 11, 23)}"""

数组

语法和 Python 的列表类似:

import tomlconfig = """# 每个数组里面的元素类型要一致integers = [1, 2, 3]colors = ["红", "黄", "绿"]nested_array_of_ints = [[1, 2], [3, 4, 5]]nested_mixed_array = [[1, 2], ["a", "b", "c"]]numbers = [0.1, 0.2, 0.5]"""data = toml.loads(config)print(data)"""{'colors': ['红', '黄', '绿'], 'integers': [1, 2, 3], 'nested_array_of_ints': [[1, 2], [3, 4, 5]], 'nested_mixed_array': [[1, 2], ['a', 'b', 'c']], 'numbers': [0.1, 0.2, 0.5]}"""

数组可以跨行,数组的最后一个值后面可以有终逗号(也称为尾逗号)。

import tomlconfig = """integers2 = [  1, 2, 3]integers3 = [  1,  2, # 这是可以的]"""data = toml.loads(config)print(data)"""{'integers2': [1, 2, 3], 'integers3': [1, 2]}"""

表,完全可以把它想象成 ini 的 section。

import tomlconfig = """# 表名的定义规则与键名相同# 解析之后得到的大字典中就有 "table-1" 这个 key# 并且其 value 也是一个表,在它下方# 直至下一个表头或文件结束,都是这个表内部的键值对[table-1]key1 = "some string"key2 = 123[table-2]key1 = "another string"key2 = 456"""data = toml.loads(config)print(data)"""{'table-1': {'key1': 'some string', 'key2': 123}, 'table-2': {'key1': 'another string', 'key2': 456}}"""

但是我们之前也实现过类似于这种结构,没错,就是点分隔符:

import tomlconfig = """# 所以 other-table-1 和 table-1 是等价的# other-table-2 和 table-2 是等价的other-table-1.key1 = "some string"other-table-1.key2 = 123other-table-2.key1 = "another string"other-table-2.key2 = 456[table-1]key1 = "some string"key2 = 123[table-2]key1 = "another string"key2 = 456"""data = toml.loads(config)print(data)"""{'other-table-1': {'key1': 'some string', 'key2': 123}, 'other-table-2': {'key1': 'another string', 'key2': 456}, 'table-1': {'key1': 'some string', 'key2': 123}, 'table-2': {'key1': 'another string', 'key2': 456}}"""

不过注意:我们必须要把 other-table-1 和 other-table-2 定义在上面,如果我们定义在下面看看会有什么后果:

import tomlconfig = """[table-1]key1 = "some string"key2 = 123[table-2]key1 = "another string"key2 = 456other-table-1.key1 = "some string"other-table-1.key2 = 123other-table-2.key1 = "another string"other-table-2.key2 = 456"""data = toml.loads(config)print(data)"""{    'table-1': {'key1': 'some string', 'key2': 123},    'table-2': {'key1': 'another string',                'key2': 456,                'other-table-1': {'key1': 'some string',                                   'key2': 123},                'other-table-2': {'key1': 'another string',                                   'key2': 456}}}"""

你可能已经猜到了,它们被视为了“table-2”对应的字典键。此外我们还可以将上面两种方式结合起来:

import tomlconfig = """# [] 里面的不再是一个普通的键,而是点分隔键# 另外键名周围的空格会被忽略,但是最好不要有[dog  .  "tater.man"]  type.name = "哈巴狗""""data = toml.loads(config)print(data)"""{    'dog': {'tater.man': {'type': {'name': '哈巴狗'}}}}"""

表的里面也是可以没有键值对的:

import tomlconfig = """[x.y.z.w.a.n][x.m][x.n][x]a.b.c = "xxx""""data = toml.loads(config)print(data)"""{'x':    {        'a': {'b': {'c': 'xxx'}},        'm': {},        'n': {},        'y': {'z': {'w': {'a': {'n': {}}}}}    }}"""

总的来说还是蛮强大的,但是要注意:不能重复定义。

行内表

行内表提供了一种更为紧凑的语法来表示表,因为上面每一个键值对都需要单独写一行,比如:

[table1]a = 1b = 2c = 3# 最终可以得到 # {'table1': {'a': 1, 'b': 2, 'c': 3}}

但是除了上面的表达方式之外,我们还可以采用行内表:

import tomlconfig = """# 和 Python 字典的表示方式略有不同# 并且也支持多种 keytable1 = {a = 1, b = "二", c.a = "3"}table2 = {c."b c".d = "4"}"""data = toml.loads(config)print(data)"""{    'table1': {'a': 1, 'b': '二', 'c': {'a': '3'}},    'table2': {'c': {'b c': {'d': '4'}}}}"""

表数组

然后来看看数组和表的结合:

import tomlconfig = """[name1]girl = "古明地觉"[[name2]]girl = "古明地恋"[name3][[name4]]"""data = toml.loads(config)print(data)"""{'name1': {'girl': '古明地觉'}, 'name2': [{'girl': '古明地恋'}], 'name3': {}, 'name4': [{}]}"""

当使用 [[]] 的时候,相当于在 [] 的基础上套上一层列表。任何对表数组的引用都会指向该数组中最近定义的表元素,这使得我们能够在最近的表内定义子表甚至子表数组。

我们再举个更复杂的例子:

import tomlconfig = """[[fruits]]  name = "苹果"  # 会操作 [] 里面最近定义的 {}[fruits.physical]  color = "红色"shape = "圆形"[[fruits.varieties]]  # 嵌套表数组name = "蛇果"    [[fruits.varieties]]name = "澳洲青苹" [[fruits]]name = "香蕉" [[fruits.varieties]]name = "车前草"  """data = toml.loads(config)print(data)"""{    'fruits':        [            {                'name': '苹果',                'physical': {'color': '红色',                              'shape': '圆形'},                'varieties': [{'name': '蛇果'},                               {'name': '澳洲青苹'}]            },            {                'name': '香蕉',                 'varieties': [{'name': '车前草'}]            }        ]}"""

很明显这种定义不是很常用,配置文件应该要非常直观才对,但这已经不是很好理解了。

以上就是怎么用Python解析toml配置文件的详细内容,更多请关注Gxl网其它相关文章!

本类排行

今日推荐

热门手游