PyYAML转储格式

问题描述 投票:30回答:3

我知道在SO上有一些关于这个的问题,但我找不到我想要的东西。

我正在使用pyyaml读取(.load().yml文件,修改或添加密钥,然后再次编写它(.dump())。问题是我想在转储后保留文件格式,但它会改变。

例如,我编辑关键en.test.index.few"Bye"而不是"Hello"

蟒蛇:

with open(path, 'r', encoding = "utf-8") as yaml_file:
    self.dict = pyyaml.load(yaml_file)

然后,更改密钥后:

with open(path, 'w', encoding = "utf-8") as yaml_file:
    dump = pyyaml.dump(self.dict, default_flow_style = False, allow_unicode = True, encoding = None)
    yaml_file.write( dump )

YAML:

之前:

en:
  test:
    new: "Bye"
    index:
      few: "Hello"
  anothertest: "Something"

后:

en:
  anothertest: Something
  test:
    index:
      few: Hello
    new: Bye

有没有办法保持相同的格式?,例如报价和订单。我使用了错误的工具吗?

我知道也许原始文件不完全正确,但我无法控制它(这是一个Ruby on Rails i18n文件)。

非常感谢你。

python python-3.x yaml quotes pyyaml
3个回答
72
投票

请改用ruamel.yaml

图书馆战斗!两个图书馆的故事

PyYAML是effectively dead,已经有好几年了。更复杂的是,http://pyyaml.org的官方项目似乎最近被取消了。该网站托管了PyYAML问题跟踪器,文档和下载。在撰写本文时,一切都已消失。这简直就是灾难性的。欢迎来到开源的另一天。

ruamel.yamlactively maintained。与PyYAML不同,ruamel.yaml支持:

  • YAML <= 1.2。 PyYAML仅支持YAML <= 1.1。这是至关重要的,因为YAML 1.2在几个边缘情况下故意使用YAML 1.1进行breaks backward compatibility。这通常是件坏事。在这种情况下,这使YAML 1.2成为JSON的严格超集。由于YAML 1.1不是JSON的严格超集,因此这是一件好事。
  • 往返保存。当调用yaml.dump()来转储先前调用yaml.load()所加载的字典时: PyYAML天真地忽略了所有输入格式 - 包括注释,排序,引用和空格。像这么多的数字垃圾一样丢弃到最近的可用位桶。 ruamel.yaml巧妙地尊重所有输入格式。一切。整个风格的辣酱玉米饼馅。整个文学shebang。所有。

图书馆迁移:代码之泪的踪迹

由于ruamel.yaml是一个PyYAML分支,因此符合PyYAML API,因此在现有应用程序中从PyYAML切换到ruamel.yaml通常就像替换所有实例一样简单:

# This imports PyYAML. Stop doing this.
import yaml

...有了这个:

# This imports "ruamel.yaml". Always do this.
from ruamel import yaml

而已。

不需要进行其他更改。 yaml.load()yaml.dump()函数应该继续按预期运行 - 现在支持YAML 1.2并积极接收错误修复的额外好处。

往返保护及其能为您做些什么

为了向后兼容PyYaml,yaml.load()yaml.dump()函数默认不执行往返保存。为此,请明确传递:

  • Loader=ruamel.yaml.RoundTripLoader的可选yaml.load()关键字参数。
  • Dumper=ruamel.yaml.RoundTripDumper的可选yaml.dump()关键字参数。

一个从ruamel.yaml documentation“借来”的例子:

import ruamel.yaml

inp = """\
# example
name:
  # Yet another Great Duke of Hell. He's not so bad, really.
  family: TheMighty
  given: Ashtaroth
"""

code = ruamel.yaml.load(inp, Loader=ruamel.yaml.RoundTripLoader)
code['name']['given'] = 'Astarte'  # Oh no you didn't.

print(ruamel.yaml.dump(code, Dumper=ruamel.yaml.RoundTripDumper), end='')

它完成了。现在将完整保留注释,排序,引用和空白。

tl;博士

始终使用ruamel.yaml。永远不要使用PyYAML。 ruamel.yaml生活。 PyYAML是一个腐烂的尸体,在PyPi的砾石地面腐烂。

ruamel.yaml万岁。


2
投票

第一

使用以下代码表示字典数据:

mapping = list(mapping.items())
    try:
        mapping = sorted(mapping)
    except TypeError:
        pass

这就是订购改变的原因

第二

有关标量类型如何呈现(带双引号或不带引号)的信息在读取时丢失(这是库的主要方法)

摘要

您可以基于'Dumper'创建自己的类,并重载方法'represent_mapping'以更改字典将如何呈现的行为

为了保存关于标量的双引号的信息,你还必须根据'Loader'创建自己的类,但我担心它会影响和其他类,并且会很难


0
投票

在我的情况下,如果值包含"{,我想要},否则没有。例如:

 en:
   key1: value is 1
   key2: 'value is {1}'

要执行该操作,请从模块PyYaml中的文件representer.py复制函数represent_str(),如果字符串包含{},则使用另一种样式:

def represent_str(self, data):
    tag = None
    style = None
    # Add these two lines:
    if '{' in data or '}' in data:
        style = '"'
    try:
        data = unicode(data, 'ascii')
        tag = u'tag:yaml.org,2002:str'
    except UnicodeDecodeError:
        try:
            data = unicode(data, 'utf-8')
            tag = u'tag:yaml.org,2002:str'
        except UnicodeDecodeError:
            data = data.encode('base64')
            tag = u'tag:yaml.org,2002:binary'
            style = '|'
    return self.represent_scalar(tag, data, style=style)

要在您的代码中使用它:

import yaml

def represent_str(self, data):
  ...

yaml.add_representer(str, represent_str)

在这种情况下,键和值之间没有差异,这对我来说已经足够了。如果你想要一个不同的键和值样式,用函数qazxsw poi执行相同的操作

© www.soinside.com 2019 - 2024. All rights reserved.