【问题标题】:python use Pyyaml and keep formatpython使用Pyyaml并保持格式
【发布时间】:2017-03-07 05:48:53
【问题描述】:

这是一个配置文件,我使用 PyYAML 从中更改一些值,然后我编写了一些配置,但它会改变我的格式,这让我感到困惑。

 $ results.yaml 
 nas:
     mount_dir: '/nvr'
     mount_dirs: ['/mount/data0', '/mount/data1', '/mount/data2']

# yaml.py

import yaml.py

conf = open("results.conf", "r")
results = yaml.load(conf)
conf.close()

result['nas']['mount_dirs'][0]= "haha"

with open('/home/zonion/speedio/speedio.conf', 'w') as conf:
    yaml.dump(speedio, conf, default_flow_style=False)

conf.close()

但它改变了我的格式,我该怎么办?

# cat results.conf
nas:
  mount_dir: /nvr
  mount_dirs:
  - haha
  - /mount/data1
  - /mount/data2

【问题讨论】:

  • 我们可以在nas(和以下几行)之前占用您的额外空间是因为Stack Overflow 上的格式不正确,并且您的实际缩进是四个空格吗?顺便说一句,如果你import yaml.py,你会得到一个ImportError

标签: python yaml


【解决方案1】:

如果您使用 ruamel.yaml ¹,您可以通过在 StackOverlow 上结合 thisthis 答案来相对轻松地实现此目的。

默认情况下,ruamel.yaml 标准化为缩进 2,并删除多余的引号。由于您似乎不希望这样,您必须明确设置缩进,或者让ruamel.yaml 分析输入,并告诉它保留引号:

import sys
import ruamel.yaml
import ruamel.yaml.util

yaml_str = """\
nas:
    mount_dir: '/nvr'
    mount_dirs: ['/mount/data0', '/mount/data1', '/mount/data2']
"""

result, indent, block_seq_indent = ruamel.yaml.util.load_yaml_guess_indent(
    yaml_str, preserve_quotes=True)
result['nas']['mount_dirs'][0] = "haha"
ruamel.yaml.round_trip_dump(result, sys.stdout, indent=indent,
                            block_seq_indent=block_seq_indent)

您可以使用 load_yaml_guess_indent() 调用来代替:

result = ruamel.yaml.round_trip_load(yaml_str, preserve_quotes=True)
indent = 4
block_sequence_indent = None 

如果您希望haha 在输出中被(单)引用,请将其设为SingleQuotedScalarString

result['nas']['mount_dirs'][0] = \
       ruamel.yaml.scalarstring.SingleQuotedScalarString("haha")

输出将是:

nas:
    mount_dir: '/nvr'
    mount_dirs: ['haha', '/mount/data1', '/mount/data2']

(鉴于您的简短示例输入没有块样式序列,block_sequence_indent 无法确定并且将为无)


使用较新的 API 时,您可以分别控制映射和序列的缩进:

yaml = ruamel.yaml.YAML()
yaml.indent(mapping=4, sequence=6, offset=3)  # not that that looks nice
data = yaml.load(some_stream)
yaml.dump(data, some_stream)

这将使您的 YAML 格式一致(如果一开始不是这样),并且在第一次往返之后不再进行任何更改。


¹ 免责声明:我是该软件包的作者。

【讨论】:

  • 这是什么版本的 ruamel?因为它不能在我办公室的机器上工作,即0.10.12
  • @ChangZhao "is this" 有点不具体,但如果您指的是 2017 年 6 月随 0.15.1 引入的新 API。我不确定您为什么使用 0.10.12,那是从 2015 年 IIRC 开始,从那时起,ruamel.yaml 已经发布了 100 多个版本。
  • 当我在 python shell 中输入 ruamel 时导入 ruamel 之后,我得到 <module 'ruamel' (built-in)> 所以 ruamel 是内置的?从那以后似乎 ruamel 就再也没有安装或更新过?
  • 你应该做一个pip install -U ruamel.yaml
【解决方案2】:

ruamel.yaml不幸的是完全保留原始格式,引用其docs

虽然没有保留行的单个缩进,但您可以 为映射和序列指定单独的缩进级别 (序列计数不包括序列的破折号 元素)和其中的块序列破折号的特定偏移量 缩进。

我不知道有任何 Python 库可以做到这一点。

当我需要更改 YAML 文件而不触及其格式时,我不情愿地使用正则表达式(不情愿地因为它几乎和 parsing XHTML with it 一样糟糕)。

如果您知道,请随时提出更好的解决方案,我很乐意了解它!

【讨论】:

  • 如果您对我投反对票,请添加评论,解释为什么我以有害的方式错了。谢谢!
【解决方案3】:

ruamel实现了一个往返的loader和dumper,试试:

import ruamel.yaml
conf = open("results.conf", "r")
results = ruamel.yaml.load(conf, ruamel.yaml.RoundTripLoader)
conf.close()
results['nas']['mount_dirs'][0] = "haha"
with open('/home/zonion/speedio/speedio.conf', 'w') as conf:
  ruamel.yaml.dump(results, conf, ruamel.yaml.RoundTripDumper)

【讨论】:

  • 谢谢你的帮助,但是它让我的单引号消失了! 像:[哈哈,/mount/data1,/mount/data2],顺便说一下,缩进也不对.
  • 那么 ruamel 并不像它宣传的那样往返。您可以通过向转储添加参数来自定义其输出;在您的情况下,indent=4, default_style="'" 可能会有所帮助(但也可能导致 all 标量被引用)。请注意,往返不是 YAML 旨在做的事情,并且尝试这样做违反了 YAML 规范。真正的答案可能是“如果你想要往返,YAML 不是正确的工具”。
猜你喜欢
  • 2020-05-10
  • 2020-06-17
  • 2014-01-15
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多