【问题标题】:parse YAML with line numbers for all keys使用所有键的行号解析 YAML
【发布时间】:2020-01-15 13:25:14
【问题描述】:

我想解析一个结构如下的 YAML 文件:

key1:
    key2: 10
    key3:
        key4:
            key5:
                "value1"
                "value2"

目前,我使用以下代码:

data = yaml.load(file, Loader=ruamel.yaml.RoundTripLoader)
print(data['key1'].lc.line) #I get line number of key1
print(data['key1']['key3'].lc.line) #I get line number of key3
print(data['key1']['key3']['key4'].lc.line) #I get line number of key4

现在我无法获取 key2key5 的行号。我发现问题是由于data[key][key2] 不是字典,而是int。同样,data[key1][key3][key4][key5] 是一个列表而不是字典。

在这种情况下,有没有办法获取 所有 键的行号?

【问题讨论】:

  • 您的代码不完整,没有导入语句,您的 YAML 无效。

标签: python ruamel.yaml


【解决方案1】:
key1:
  key2: 10
  key3:
    key4:
      key5:
        - "value1"
        - "value2"
      key6: 20
      key7:
        key8:
          - "value3"
          - "value4"
import yaml

with open('test.yml') as f:
    data = yaml.full_load(f)

result = {}
current = 1


def keyId(val, current):
    ks = val.keys()
    for key in ks:
        result[key] = current
        current += 1
        if isinstance(val[key], type([])):
            current += len(val[key])
        if isinstance(val[key], type({})):
            keyId(val[key], current)

    return


keyId(data, current)
print(result)
{'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4, 'key5': 5, 'key6': 8, 'key7': 9, 'key8': 10}

【讨论】:

  • 谢谢。但是一旦 yaml 文件中有空行,例如 key6 和 key7 之间,就会出现问题。此外,在我加载文件之前,您的代码对我不起作用:data = yaml.load(f, Loader=ruamel.yaml.RoundTripLoader)
  • @Mayank ruamel.yaml,没有full_load() 功能。您似乎正在使用 PyYAML,但没有表明 OP 需要将他的(真实)输入降级为 YAML 1.1,于 2009 年被替换。自 2006 年以来,YAML 文件的推荐扩展名一直是.yaml。欢迎来到 20 年代!
  • @fatiha.kafou 要解决空行问题,您可以采用这种方法.. 1st 从您的 yaml 文件中获取所有密钥.. 存储密钥以便将它们放入数组中..然后将 yaml 读取为文本并逐行读取以获取确切的行号。
  • @fatiha.kafou 我已经添加了另一个答案来做你想做的事情,并考虑换行
【解决方案2】:

这在多个层面都是错误的:

  • 您正在使用旧的ruamel.yaml 接口,并且在您的代码中同时包含yaml.ruamel.yaml。 您是否将 RoundTripLoader 与 PyYAML 一起使用?

  • 您的输入不是有效的 YAML,因为您不能使用双引号 标量 ("value1") 后跟另一个(您当然可以在 Python)。既然你说的是“是一个列表”,但是没有序列 在将创建一个列表的输入中,您可能已经遗漏了 破折号。

  • 您将获得的数字与键的行号混淆了 在您的各种映射中。虽然可能很难看到 计数从 0 开始,很明显,如果你添加一个额外的 输入中的(空)行

  • 完全可以打印 data['key1']['key3']['key4']['key5'].lc.line,因为该值是 列表(子类型)虽然这当然是该列表开头的行号 不适用于key5

  • 由于data['key1']['key2']int,我不明白你是怎么做到的 可以期望一个属性指示该类型的行号(例如 如果 int 恰好是 dict 值,则它本身或某个键)。有 你曾尝试在像int 这样的内置类上设置属性吗? 有时间试试10.lc = 24


import ruamel.yaml 

yaml_str = """\
key1:
    key2: 10
    key3:

        key4:
            key5:
                - "value1"
                - "value2"
"""

yaml = ruamel.yaml.YAML()
data = yaml.load(yaml_str)
assert isinstance(data['key1']['key2'], int)
print(data['key1'].lc.line) # the starting line number of the value associated with of key1
print(data['key1']['key3'].lc.line) # the starting line number of the value associated with key3
print(data['key1']['key3']['key4'].lc.line) # the starting line number of the value of key4
print(data['key1']['key3']['key4']['key5'].lc.line) # the starting line number of the value of key5

给出:

1
4
5
6

由于输入中多了一行,输出从1 3 4 5变为1 4 5 6 应该很清楚 你没有得到的不是任何键的行号。

所以很明显,您根本没有获得键的行号

是的,有一种方法可以获取所有键的行号。
(如果您意识到您想知道如何做到这一点:您应该从方法 construct_mapping 的所有 key_node 分配中获取该信息 RoundTripLoader 类(它是子类并将其提供给 yaml 加载前的实例)并将其附加到某些属性 你自己在该方法的maptyp 变量中。)

【讨论】:

    【解决方案3】:
    key1:
      key4: 10
      key3:
        key8:
    
          key5:
            - "value1"
            - "value2"
          key6: 20
          key7:
    
            key2:
              - "value3"
              - "value4"
    
    with open('test.yml') as f:
        lines = list(map(lambda x: x.strip().split(':')[0] if x.find(':') != -1 else "", f.readlines()))
        print(lines)
    
    ['key1', 'key4', 'key3', 'key8', '', 'key5', '', '', 'key6', 'key7', '', 'key2', '', '']
    

    这里每个键都存储在 yml 文件中出现的索引处

    【讨论】:

    • 一个 YAML 文件可以在一行中有多个键。标量可以嵌入冒号。您的代码也无法处理。
    • 这段代码的另一个问题是它不能区分两个同名的键。
    猜你喜欢
    • 2012-10-30
    • 2021-06-06
    • 1970-01-01
    • 2015-12-06
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2015-06-10
    • 2020-10-14
    相关资源
    最近更新 更多