Python:替换 YAML 文件中的字符串

L.K*_*ing 4 python yaml pyyaml

我有这个示例 YAML 文件:

---
test:
  name: "Tom"
  age: "5"
  version: "1.0"
Run Code Online (Sandbox Code Playgroud)

如何将此 YAML 文件替换为:


test:
  name: "Max"
  age: "10"
  version: "2.2"
Run Code Online (Sandbox Code Playgroud)

这是我打开文件的方式:

import yaml

with open("config.yml", 'r') as stream:
        print(yaml.load(stream))
Run Code Online (Sandbox Code Playgroud)

但我现在不知道如何编辑 YAML 文件。

Adr*_*Pop 8

鉴于您使用 的事实PyYaml,执行此操作的适当方法如下所示:

#!/usr/bin/env python

import yaml

with open("testfile.yaml", 'r') as stream:
    try:
        loaded = yaml.load(stream)
    except yaml.YAMLError as exc:
        print(exc)

# Modify the fields from the dict
loaded['test']['name'] = "Max"
loaded['test']['age'] = "10"
loaded['test']['version'] = "2.2"

# Save it again
with open("modified.yaml", 'w') as stream:
    try:
        yaml.dump(loaded, stream, default_flow_style=False)
    except yaml.YAMLError as exc:
        print(exc)
Run Code Online (Sandbox Code Playgroud)

因此,您只需将 yaml 加载到被dict调用的文件中loaded,修改所需的值,然后保存它(是否覆盖原始文件,您的调用)。对于嵌套输入,您将有一个必须修改的嵌套字典。该default_flow_style=False参数对于生成您想要的格式(流样式)是必需的,否则对于嵌套集合,它会生成块样式:

A: a
B: {C: c, D: d, E: e}
Run Code Online (Sandbox Code Playgroud)

干杯!

稍后编辑

正如安东指出的,我的回答有一些缺陷。

  • 最好使用safe_load而不是load因为后者有潜在的危险。

  • 输出需要一个指令结束指示符(开头的三个破折号)。为了附加它们,我们explicit_start=Truedump方法中使用(实际上应该是safe_dump)。

  • 如果您想生成更好的输出,请使用 Mayberuamel.yaml代替(尽管它们在语义上是相同的)yaml

有关更详细的信息,请参阅 Anthon 的答案,因为他是该包的作者。


Ant*_*hon 6

如果您通读 PyYAML 文档,您会发现它\n告诉您使用该load()函数有潜在危险,\n因此要做的第一件事(因为您和几乎其他人都不需要\n)不是使用它,但使用safe_load()代替。

\n\n

您还应该将输入文件更改为config.yaml,自 2006 年起,\n就开始推荐YAML 文件的扩展名.yaml

\n\n

config.yaml知道了这一点,使用 PyYAML更改文件的方法:

\n\n
import yaml\n\nwith open(\'config.yaml\') as stream:\n   data = yaml.safe_load(stream)\n\ntest = data[\'test\']\ntest.update(dict(name="Tom", age="10", version="2.2"))\n\nwith open(\'output.yaml\', \'wb\') as stream:\n   yaml.safe_dump(data, stream, default_flow_style=False, \n                  explicit_start=True, allow_unicode=True, encoding=\'utf-8\')\n
Run Code Online (Sandbox Code Playgroud)\n\n

这会给你一个output.yaml看起来像这样的:

\n\n
---\ntest:\n  age: \'10\'\n  name: Tom\n  version: \'2.2\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

default_flow_style参数对于不获取类似 JSON 的叶节点映射\n结构是必需的。来explicit_start获取\n前导指令结束指示符 ( ---),我建议始终使用\n (并将文件作为二进制文件打开)\n,以便在更改为\nallow_unicode=True, encoding=\'utf-8\'时不会遇到意外或问题。nameBj\xc3\xb6rk Gu\xc3\xb0mundsd\xc3\xb3ttir

\n\n

现在您会注意到,这不会生成您想要的输出(尽管语义上相同):

\n\n
    \n
  • 字符串周围的单引号而不是双引号可以解释为数字
  • \n
  • 周围没有双引号Tom
  • \n
  • 对映射的键进行排序
  • \n
\n\n

如果您在 YAML 文件中有任何注释,这些注释将会丢失。

\n\n

更新 YAML 文件的更好方法是使用ruamel.yaml\n(免责声明:我是该包的作者),它比 PyYAML 有更多\ns任何默认值,处理 YAML 1.2 并且不会删除注释\n(如果您有它们在你的文件中):

\n\n
import ruamel.yaml\n\n\nyaml = ruamel.yaml.YAML()\nyaml.preserve_quotes = True\nyaml.explicit_start = True\n\nwith open(\'config.yaml\') as stream:\n   data = yaml.load(stream)\n\ntest = data[\'test\']\ntest.update(dict(name="Tom", age="10", version="2.2"))\n\nwith open(\'output.yaml\', \'wb\') as stream:\n    yaml.dump(data, stream)\n
Run Code Online (Sandbox Code Playgroud)\n\n

这样你的输出文件将是:

\n\n
---\ntest:\n  name: "Tom"\n  age: "10"\n  version: "2.2"\n
Run Code Online (Sandbox Code Playgroud)\n\n

这正是你想要的。

\n