【问题标题】:How to deal with Hex values in YAML using both Go and Python?如何使用 Go 和 Python 处理 YAML 中的十六进制值?
【发布时间】:2019-02-08 01:21:41
【问题描述】:

我有这个简单的以下程序:

package main

import (
    "fmt"

    yaml "gopkg.in/yaml.v2"
)

type Test struct {
    SomeStringWithQuotes string `yaml:"someStringWithQuotes"`
    SomeString           string `yaml:"someString"`
    SomeHexValue         string `yaml:"someHexValue"`
}

func main() {
    t := Test{
        SomeStringWithQuotes: "\"Hello World\"",
        SomeString:           "Hello World",
        SomeHexValue:         "0xDef9C64256DeE61ebf5B212238df11C7E532e3B7",
    }
    yamlBytes, _ := yaml.Marshal(t)
    fmt.Print(string(yamlBytes))
}

这会打印以下内容,并且显然表明 Go 决定何时引用字符串:

someStringWithQuotes: '"Hello World"'
someString: Hello World
someHexValue: 0xDef9C64256DeE61ebf5B212238df11C7E532e3B7

但是,当我尝试使用以下 Python 脚本读取此 YAML 时:

import yaml

yaml_str = """
someStringWithQuotes: '"Hello World"'
someString: Hello World
someHexValue: 0xDef9C64256DeE61ebf5B212238df11C7E532e3B7
"""

print(yaml.load(yaml_str))

它将十六进制值解析为整数。如果我现在使用此代码序列化回 YAML:

import yaml
import sys

yaml_str = """
someStringWithQuotes: '"Hello World"'
someString: Hello World
someHexValue: 0xDef9C64256DeE61ebf5B212238df11C7E532e3B7
"""

print(yaml.dump(yaml.load(yaml_str)))

我明白了:

someHexValue: 1272966107484048169783147972546098614451903325111
someString: Hello World
someStringWithQuotes: '"Hello World"'

我怎样才能最好地确保保留十六进制格式?不幸的是,我个人对 Go 端的代码没有任何影响(但是对于尝试做类似事情的其他人来说,仍然欢迎 Go 端的解决方案)。

【问题讨论】:

    标签: python go yaml


    【解决方案1】:

    您可以在 Python 中加载和转储该输出,同时使用 ruamel.yaml 保留十六进制值(免责声明:我是该 Python 包的作者):

    import sys
    import ruamel.yaml
    
    yaml_str = """\
    someHexValue: 0xDef9C64256DeE61ebf5B212238df11C7E532e3B7
    someString: Hello World
    someStringWithQuotes: '"Hello World"'
    """
    
    yaml = ruamel.yaml.YAML()
    data = yaml.load(yaml_str)
    yaml.dump(data, sys.stdout)
    

    给出:

    someHexValue: 0xDEF9C64256DEE61EBF5B212238DF11C7E532E3B7
    someString: Hello World
    someStringWithQuotes: '"Hello World"'
    

    go的实际输出是不正确的,如果你用Python输出字符串"0xDef9C64256DeE61ebf5B212238df11C7E532e3B7",那么你会看到它输出 那个带引号的字符串(我在这里使用ruamel.yaml,但这对PyYAML来说是一样的):

    import sys
    import ruamel.yaml
    
    data = dict(someHexValue="0xDef9C64256DeE61ebf5B212238df11C7E532e3B7")
    
    yaml = ruamel.yaml.YAML()
    yaml.dump(data, sys.stdout)
    

    给出:

    someHexValue: '0xDef9C64256DeE61ebf5B212238df11C7E532e3B7'
    

    这个字符串需要引用,是由表示 字符串“plain”(即不带引号),然后尝试解决它 确保返回原始类型(字符串)。这不是 情况下,因为它被发现是一个整数和代表部分 倾销过程决定报价是必要的。 (如果你看过 在加载和转储代码时,想知道为什么要使用解析器 两者兼而有之:这就是倾销者需要访问resolver.py 的原因 好)。

    这对于 "True" 和 "2019-02-08' 等字符串的工作方式相同,它们也会被引用(按顺序 不要将它们与布尔值或日期“混淆”)。

    这是一个相当昂贵的计算过程,当然还有其他方法 确定是否需要引号。

    在go中也是这样,但是resolve.go中的相关代码有错误:

            intv, err := strconv.ParseInt(plain, 0, 64)
            if err == nil {
                if intv == int64(int(intv)) {
                    return yaml_INT_TAG, int(intv)
                } else {
                    return yaml_INT_TAG, intv
                }
            }
    

    来自ParseInt的文档:

    如果 base == 0,则字符串前缀隐含了 base:base 16 表示“0x”,base 8 表示“0”,否则为 base 10。

    问题当然是在 YAML 和 Python 关于整数的大小。但是在 go 中限制为 64 位。所以在上面 ParseInt 返回一个错误并且 go 认为 字符串不需要引用。 (我将此报告为 bug 在 go-yaml 中 图书馆)。

    去Marshall 函数似乎没有像你一样强制引用的标志 设置yaml.default_style = '"'` inruamel.yaml``。

    【讨论】:

      【解决方案2】:

      Go 将该十六进制字符串解释为数字。

      someHexValue: 0xDef9C64256DeE61ebf5B212238df11C7E532e3B7
      

      如果这是它生成的 yaml,那么 python 将其视为数字是正确的。

      在 python 中的一个创可贴是使用将其转换回十六进制

      hex(1272966107484048169783147972546098614451903325111)
      

      这里是yaml spec,将十六进制视为数字

      【讨论】:

      • 与您的说法相反,go 不会将该十六进制字符串解释为数字,因为它太大而无法适应其内部(64 位)表示。这就是为什么它认为(错误地)它可以将该字符串输出为纯标量(即不带引号)。 Python 正确地将纯标量解释为十六进制,它应该。 (您还可以参考 go-yaml 不支持的旧 YAML 规范(1.0)。)
      • @Anthon 感谢您的澄清。我不知道 golang.go 方面的有趣错误
      • 我也不懂 golang,所以花了一段时间才找到导致该错误的原因。幸运的是,解析器的结构与我的 ruamel.yaml 解析器一样,这很有帮助:-)
      • tl;dr: 只需使用 ruamel.yaml,就像 Anthon 的 answer below advises。这是 2019 年。除了遗留应用程序之外,没有人应该使用 PyYAML。 PyYAML codebase 已经奄奄一息了近一年,坦率地说,在所有可能的方面,ruamel.yaml 的稳定性、效率和功能集都相形见绌。
      猜你喜欢
      • 2013-09-11
      • 2012-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-04
      • 2011-07-07
      • 2011-04-26
      • 1970-01-01
      相关资源
      最近更新 更多