往返前导0的十六进制数

时间:2017-04-15 15:19:58

标签: python yaml ruamel.yaml

我愿意加载一个包含32位十六进制数字的yaml文件,并保留前导0以使该数字始终为0xXXXXXXXX格式。

我创建了一个自定义类和表示符,以便可以在此表单中转储十六进制数字:

class HexWInt(int):
    pass    

def represent_HexWInt(self, data):
    # type: (Any) -> Any
    return self.represent_scalar(u'tag:yaml.org,2002:int', '0x' + format(data, 'x').upper().zfill(8))

yaml.RoundTripRepresenter.add_representer(HexWInt, represent_HexWInt)

但是,我找不到将此格式应用于往返十六进制数字的正确方法。

确实如下:

yamltext = "hexa: 0x0123ABCD"
code = yaml.round_trip_load(yamltext)
yaml.dump(code, sys.stdout, Dumper=yaml.RoundTripDumper)

显示

hexa: 0x123ABCD

我希望在哪里显示

hexa: 0x0123ABCD

如何强制使用十六进制数字以适合0xXXXXXXXX格式?

1 个答案:

答案 0 :(得分:0)

有多种方法可以做你想要的。如果您不想影响解析器的正常行为,则应该使用备用RoundTripLoaderRoundTripConstructorRoundTripConstructorRoundTripRepresenter子类化。但这需要注册所有构造函数和表示者,并且非常详细。

如果您不关心是否能够在程序中加载具有带原始功能的前导零的十六进制标量整数的其他YAML文档,则可以添加一个新的RoundTripConstructorRoundTripRepresenter的构造函数和表示者。

最简单的部分是根据值和宽度获取格式。如果您正在使用zfill(),则不需要upper()format

'0x{:0{}X}'.format(value, width)

does the job

您的代码无效的主要原因是您的代码永远不构造HexWInt,因为RoundTripLoader不知道它应该这样做。我也不会将宽度硬编码为8,而是从输入中导出(使用len()),并保留它。

import sys
import ruamel.yaml

class HexWInt(ruamel.yaml.scalarint.ScalarInt):
    def __new__(cls, value, width):
        x = ruamel.yaml.scalarint.ScalarInt.__new__(cls, value)
        x._width = width   # keep the original width
        return x

    def __isub__(self, a):
        return HexWInt(self - a, self._width)

def alt_construct_yaml_int(constructor, node):
    # check for 0x0 starting hex integers
    value_s = ruamel.yaml.compat.to_str(constructor.construct_scalar(node))
    if not value_s.startswith('0x0'):
        return constructor.construct_yaml_int(node)
    return HexWInt(int(value_s[2:], 16), len(value_s[2:]))

ruamel.yaml.constructor.RoundTripConstructor.add_constructor(
    u'tag:yaml.org,2002:int', alt_construct_yaml_int)

def represent_hexw_int(representer, data):
    return representer.represent_scalar(u'tag:yaml.org,2002:int',
                                        '0x{:0{}X}'.format(data, data._width))

ruamel.yaml.representer.RoundTripRepresenter.add_representer(HexWInt, represent_hexw_int)

yaml_text = """\
hexa: 0x0123ABCD
hexb: 0x02AD
"""
yaml = ruamel.yaml.YAML()
data = yaml.load(yaml_text)
data['hexc'] = HexWInt(0xa1, 8)
data['hexb'] -= 3
yaml.dump(data, sys.stdout)

HexWInt存储值和宽度。 alt_construct_yaml_int将所有内容传递给原始construct_yaml_int,但标量以0x0开头的情况除外。它基于解析器完成的正常正则表达式匹配在add_constructor()注册。表示器将值和宽度组合回一个字符串。以上的输出是:

hexa: 0x0123ABCD
hexb: 0x02AD
hexc: 0x000000A1

请注意,您不能执行以下操作:

data['hexb'] -= 3

as ScalarInt(确实有方法__isub__)不知道width属性。要使上述工作正常,您必须实施适当的方法,例如ScalarInt,作为HexWInt上的方法。 E.g:

    def __isub__(self, a):
        return HexWInt(self - a, self._width)

上面的增强版本(在整数中也保留_并支持八进制和二进制整数)包含在ruamel.yaml>=0.14.7