防止YAML中的十六进制字符串在Python中作为float / int加载

时间:2017-06-03 21:12:13

标签: python yaml

我尝试使用python在YAML文件中进行一些更改。 我有一个结构文件:

name:
  mode: value

该值是十六进制数字的数字。 我这样做:

import ruamel.yaml as yaml
f = open('C:\\file.yaml', 'r')
data = yaml.load(f, Loader=yaml.RoundTripLoader)

然后更改几个值并将其转储回来:

with open('C:\\file.yaml', 'w') as out_yaml:
     yaml.dump(in_data, out_yaml, Dumper=yaml.RoundTripDumper, explicit_start=True)

这里我有一个问题 - 一些值被加载为int或float,并且在转储之后我得到诸如.inf9.4014e+40之类的值,例如来自YAML:

name:
  mode1: 8578E877
  mode2: 94014E36

如何保留此类转换的结果值?

1 个答案:

答案 0 :(得分:2)

你声明"该值是一些十六进制数"和"一些值被加载为int或float"。根据用于往返加载的YAML 1.2. Core Schema(以及更简单的JSON模式):这些标量 intfloat而非字符串。

如果您尝试将具有各种字符组合的结构转储为字符串:

import sys
from ruamel import yaml

data = dict(
    name=dict(
        teny='decade',
        tbig='8578E877',
        mode='94014E36',
        ints='12345',
        Bool='True',
    )
)
yaml.round_trip_dump(data, sys.stdout)
你会得到:

name:
  teny: decade
  tbig: '8578E877'
  mode: '94014E36'
  ints: '12345'
  Bool: 'True'

正如您所看到的,只有decade将其标记为不带引号的标量,其原因在于,如果没有引用其他标题,则可能会误解其他标记。

这就是为什么,如果你看看如何构建RoundTripLoaderRoundTripDumper(及其安全和不安全的对应物),你可以看到两者来自Resolver。即使在转储值时,模式中的模式也会与附加到Resolver的正则表达式进行匹配,以确保它不会作为特殊类型加载回来。由于YAML输出中引用的True表示这也适用于intfloat以外的其他类型。

简单的解决方案是在YAML输入中引用十六进制数字的组合:

import sys
from ruamel import yaml

yaml_str = """\
name:
  tbig: '8578E877'
  mode: '94014E36'
"""

data = yaml.round_trip_load(yaml_str)
print(data['name']['tbig'], data['name']['mode'], end='\n----\n')
yaml.round_trip_dump(data, sys.stdout)

将打印:

8578E877 94014E36
----
name:
  tbig: '8578E877'
  mode: '94014E36'

可以防止这些标量字符串作为int加载。 float。如果您通过更改附加到Resolver的正则表达式来执行此操作,您的程序将以不再遵循核心架构的方式加载并转储,因此引号不会附加在必要时自动转储。

如果现有YAML文件中的值太多而无法手动更改,并希望进行一次性转换,则您需要使用具有解析程序的Loader。使用这些模式。幸运的是BaseLoader这样做了:

import sys
from ruamel import yaml

yaml_str = """\
name:
  tbig: 8578E877
  mode: 94014E36
  teny: DECADE
"""

data = yaml.load(yaml_str, yaml.loader.BaseLoader)
yaml.round_trip_dump(data, sys.stdout)

给出:

name:
  tbig: '8578E877'
  mode: '94014E36'
  teny: DECADE

仅在必要时添加引号。在那次一次性抛负载之后,您可以在该文件上使用正常的往返或安全加载器。

我给出的第一个例子的YAML输出中的键与dict中给出的顺序相同。对于Python 3.6来说就是这样,但是在运行2.7时却没有。如果从Python数据结构开始并希望控制该输出顺序,请使用ruamel.yaml.comments.CommentedMap而不是dict。