PyYAML 转储格式
Posted
技术标签:
【中文标题】PyYAML 转储格式【英文标题】:PyYAML dump format 【发布时间】:2014-01-15 07:47:20 【问题描述】:我知道在 SO 上有一些关于此的问题,但我找不到我要查找的内容。
我正在使用pyyaml 读取(.load()
).yml
文件,修改或添加密钥,然后再次写入(.dump()
)。问题是我想在转储后保留文件格式,但它发生了变化。
例如,我将键 en.test.index.few
编辑为 "Bye"
而不是 "Hello"
Python:
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
有没有办法保持相同的格式?例如 qoutes 和 order。我是否为此使用了错误的工具?
我知道原始文件可能并不完全正确,但我无法控制它(它是一个 Ruby on Rails i18n 文件)。
非常感谢。
【问题讨论】:
yaml.dump
有一个 default_style
参数。使用default_style='"'
会将您的字符串值保留在双引号中,但您的键和任何其他值类型也将用双引号括起来。
谢谢!,我会记住的,如果不是钥匙,它会非常有用:(
您可能也很难订购钥匙。 yaml.load
给你一个dict
;它的键是无序的。 yaml.dump
可能以迭代进行的任何顺序输出。
新文件表示与原始文件完全相同的信息(在 YAML 中);没有理由保持相同的格式。
@Evert 没错,但我想保留格式,因为考虑到我创建的 sublime 包的上下文,它很有用github.com/NicoSantangelo/sublime-text-i18n-rails
【参考方案1】:
下面,使用ruamel.yaml
代替。
ruamel.yaml
是 actively maintained。与 PyYAML 不同,ruamel.yaml
支持:
yaml.dump()
转储先前调用 yaml.load()
加载的字典时:
PyYAML 天真地忽略所有输入格式 - 包括 cmets、排序、引用和空格。像这么多数字垃圾一样被丢弃到最近的可用比特桶中。
ruamel.yaml
巧妙地尊重所有输入格式。一切。整个风格的辣酱玉米饼馅。整个文学界。 全部。
库迁移
在现有应用程序中从 PyYAML 切换到 ruamel.yaml
通常只需将库导入更改为:
from ruamel import yaml
这是因为ruamel.yaml
是一个符合 PyYAML API 的 PyYAML 分支。
不需要进行其他更改。 yaml.load()
和 yaml.dump()
函数应继续按预期运行。
往返保存以及它可以为您做什么
为了向后兼容 PyYaml,yaml.load()
和 yaml.dump()
函数默认不执行往返保存。为此,请明确传递:
yaml.load()
的可选 Loader=ruamel.yaml.RoundTripLoader
关键字参数。
yaml.dump()
的可选 Dumper=ruamel.yaml.RoundTripDumper
关键字参数。
一个从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='')
大功告成。现在将原封不动地保留注释、排序、引用和空格。
【讨论】:
我必须说这是一个很棒的答案。我目前没有开发使用 PyYAML 的项目,但是当我有空闲时间并接受答案时,我肯定会尝试ruamel.yaml
。谢谢!
@sjmh 从 ruamel.yaml 0.11.12 开始,您可以在加载期间指定 preserve_quotes=True
,这将包装加载的字符串,其中包含转储所需的信息。另见this answer
PyYAML 现在有了新的维护者,并且最近发布了 v4.1。答案已经过时,诸如“PyYAML 是一具腐烂的恶臭尸体”之类的愚蠢内容可能应该被删除。
3.x 到 4.x 是一个主要的版本号颠簸,因此应该预料到向后不兼容的变化。我并不否认 PyYAML 维护存在问题和政治问题,但这个答案中使用的语言有点过分。它读起来像是 ruamel.yaml 或宣传的广告。
这个答案应该更正。它立即以虚假陈述开始。也许他们在写它的时候是正确的,但情况不再如此。 PyYaml 并没有死掉,网站已经上线了。在撰写此评论的那一刻,PyYaml 显得非常活跃和活跃。查看最新版本: - 2019-07-30:PyYAML 5.1.2 发布。 - 2018-06-06:PyYAML 5.1.1 发布。 - 2019-03-13:LibYAML 0.2.2 和 PyYAML 5.1 发布。 - 2018-07-05:PyYAML 3.13 发布。 - 2018-06-24:LibYAML 0.2.1 发布。这个答案具有误导性。【参考方案2】:
就我而言,如果值包含 或
,我想要
"
,否则什么都没有。例如:
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)
在这种情况下,键和值之间没有差异,这对我来说就足够了。如果您想要不同的键和值样式,请使用函数 represent_mapping
执行相同的操作
【讨论】:
【参考方案3】:第一
使用以下代码表示字典数据:
mapping = list(mapping.items())
try:
mapping = sorted(mapping)
except TypeError:
pass
这就是改变顺序的原因
第二
有关标量类型如何呈现的信息(是否使用双引号)在阅读时会丢失(这是库的主要方法)
总结
您可以基于“Dumper”创建自己的类并重载方法“represent_mapping”以更改字典的呈现方式
要保存有关标量双引号的信息,您还必须基于“加载器”创建自己的类,但我担心它会影响其他类并且会很难做到
【讨论】:
以上是关于PyYAML 转储格式的主要内容,如果未能解决你的问题,请参考以下文章
我可以在 yaml/pyyaml 中转储空白而不是 null 吗?