如何在 Python 中解析 YAML 文件
- 2024-12-25 08:51:00
- admin 原创
- 150
问题描述:
如何在 Python 中解析 YAML 文件?
解决方案 1:
不依赖 C 头文件的最简单、最纯粹的方法是 PyYaml (文档),可以通过以下方式安装pip install pyyaml
:
import yaml
with open("example.yaml") as stream:
try:
print(yaml.safe_load(stream))
except yaml.YAMLError as exc:
print(exc)
就是这样。普通yaml.load()
函数也存在,但yaml.safe_load()
应始终优先使用,以避免引入任意代码执行的可能性。因此,除非您明确需要任意对象序列化/反序列化,否则请使用safe_load
。
请注意,PyYaml 项目支持YAML 1.1 规范的版本。如果需要YAML 1.2 规范支持,请参阅此答案中所述的ruamel.yaml。
此外,您还可以使用 pyyaml 的替代品,它可以让您的 yaml 文件保持与原来相同的顺序,称为oyaml。在此处查看 oyaml 的 synk
解决方案 2:
使用 Python 2+3(和 unicode)读取和写入 YAML 文件
# -*- coding: utf-8 -*-
import yaml
import io
# Define data
data = {
'a list': [
1,
42,
3.141,
1337,
'help',
u'€'
],
'a string': 'bla',
'another dict': {
'foo': 'bar',
'key': 'value',
'the answer': 42
}
}
# Write YAML file
with io.open('data.yaml', 'w', encoding='utf8') as outfile:
yaml.dump(data, outfile, default_flow_style=False, allow_unicode=True)
# Read YAML file
with open("data.yaml", 'r') as stream:
data_loaded = yaml.safe_load(stream)
print(data == data_loaded)
创建 YAML 文件
a list:
- 1
- 42
- 3.141
- 1337
- help
- €
a string: bla
another dict:
foo: bar
key: value
the answer: 42
常见文件结尾
.yml
和.yaml
替代方案
CSV:超级简单格式(读写)
JSON:适合编写人类可读的数据;非常常用(读取和写入)
YAML:YAML 是 JSON 的超集,但更易于阅读(读写,JSON 和 YAML 的比较)
pickle:一种 Python 序列化格式(读写)⚠️ 将 pickle 与来自第三方的文件一起使用会带来无法控制的任意代码执行风险。
MessagePack(Python 包):更紧凑的表示(读取和写入)
HDF5(Python 包):适用于矩阵(读写)
XML:也存在 叹息 (读写)
对于您的应用程序,以下内容可能很重要:
其他编程语言的支持
读写性能
紧凑性(文件大小)
另请参阅:数据序列化格式比较
如果你正在寻找创建配置文件的方法,你可能需要阅读我的短文《Python 中的配置文件》
解决方案 3:
如果您有符合YAML 1.2 规范(2009 年发布)的 YAML,那么您应该使用ruamel.yaml(免责声明:我是该软件包的作者)。它本质上是 PyYAML 的超集,支持 YAML 1.1(自 2005 年起)的大部分内容。
如果您希望在往返时能够保留您的评论,那么您当然应该使用 ruamel.yaml。
升级@Jon 的示例很容易:
import ruamel.yaml as yaml
with open("example.yaml") as stream:
try:
print(yaml.safe_load(stream))
except yaml.YAMLError as exc:
print(exc)
safe_load()
除非您确实完全控制输入、需要它(很少有这种情况)并且知道自己在做什么,否则请使用。
如果您使用 pathlibPath
来操作文件,最好使用 ruamel.yaml 提供的新 API:
from ruamel.yaml import YAML
from pathlib import Path
path = Path('example.yaml')
yaml = YAML(typ='safe')
data = yaml.load(path)
解决方案 4:
首先使用 pip3 安装 pyyaml。
然后导入 yaml 模块并将文件加载到名为“my_dict”的字典中:
import yaml
with open('filename.yaml') as f:
my_dict = yaml.safe_load(f)
这就是你所需要的。现在整个 yaml 文件都在 'my_dict' 字典中。
解决方案 5:
要访问 YAML 文件中列表的任何元素,如下所示:
global:
registry:
url: dtr-:5000/
repoPath:
dbConnectionString: jdbc:oracle:thin:@x.x.x.x:1521:abcd
您可以使用以下python脚本:
import yaml
with open("/some/path/to/yaml.file", 'r') as f:
valuesYaml = yaml.load(f, Loader=yaml.FullLoader)
print(valuesYaml['global']['dbConnectionString'])
解决方案 6:
例子:
默认值.yaml
url: https://www.google.com
环境.py
from ruamel import yaml
data = yaml.safe_load(open('defaults.yaml'))
data['url']
解决方案 7:
我使用ruamel.yaml。详细信息和争论请见此处。
from ruamel import yaml
with open(filename, 'r') as fp:
read_data = yaml.load(fp)
ruamel.yaml的使用与 PyYAML 的旧用法兼容(存在一些简单可解决的问题),并且正如我提供的链接中所述,使用
from ruamel import yaml
而不是
import yaml
它将解决你的大部分问题。
编辑:事实证明,PyYAML 并没有死,只是在不同的地方维护。
解决方案 8:
我建议将该pyyaml
库与内置pathlib
库一起使用。
您首先需要构建一个pathlib.Path
对象:
from pathlib import Path
import yaml
path: Path = Path("/tmp/file.yaml")
# Make sure the path exists
assert path.exists()
# Read file and parse with pyyaml
dictionnaire = yaml.safe_load(path.read_text())
这与 Python 3 的现代版本兼容。
这样就不需要使用open
文件方法了。代码更简洁一些,在实际尝试解析 YAML 文件之前,您可以轻松检查文件是否存在。
解决方案 9:
我为此编写了自己的脚本。请随意使用,只要您保留署名。该脚本可以从文件解析 yaml(函数load
),从字符串解析 yaml(函数loads
)并将字典转换为 yaml(函数dumps
)。它尊重所有变量类型。
# © didlly AGPL-3.0 License - github.com/didlly
def is_float(string: str) -> bool:
try:
float(string)
return True
except ValueError:
return False
def is_integer(string: str) -> bool:
try:
int(string)
return True
except ValueError:
return False
def load(path: str) -> dict:
with open(path, "r") as yaml:
levels = []
data = {}
indentation_str = ""
for line in yaml.readlines():
if line.replace(line.lstrip(), "") != "" and indentation_str == "":
indentation_str = line.replace(line.lstrip(), "").rstrip("
")
if line.strip() == "":
continue
elif line.rstrip()[-1] == ":":
key = line.strip()[:-1]
quoteless = (
is_float(key)
or is_integer(key)
or key == "True"
or key == "False"
or ("[" in key and "]" in key)
)
if len(line.replace(line.strip(), "")) // 2 < len(levels):
if quoteless:
levels[len(line.replace(line.strip(), "")) // 2] = f"[{key}]"
else:
levels[len(line.replace(line.strip(), "")) // 2] = f"['{key}']"
else:
if quoteless:
levels.append(f"[{line.strip()[:-1]}]")
else:
levels.append(f"['{line.strip()[:-1]}']")
if quoteless:
exec(
f"data{''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}[{key}]"
+ " = {}"
)
else:
exec(
f"data{''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}['{key}']"
+ " = {}"
)
continue
key = line.split(":")[0].strip()
value = ":".join(line.split(":")[1:]).strip()
if (
is_float(value)
or is_integer(value)
or value == "True"
or value == "False"
or ("[" in value and "]" in value)
):
if (
is_float(key)
or is_integer(key)
or key == "True"
or key == "False"
or ("[" in key and "]" in key)
):
exec(
f"data{'' if line == line.strip() else ''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}[{key}] = {value}"
)
else:
exec(
f"data{'' if line == line.strip() else ''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}['{key}'] = {value}"
)
else:
if (
is_float(key)
or is_integer(key)
or key == "True"
or key == "False"
or ("[" in key and "]" in key)
):
exec(
f"data{'' if line == line.strip() else ''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}[{key}] = '{value}'"
)
else:
exec(
f"data{'' if line == line.strip() else ''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}['{key}'] = '{value}'"
)
return data
def loads(yaml: str) -> dict:
levels = []
data = {}
indentation_str = ""
for line in yaml.split("
"):
if line.replace(line.lstrip(), "") != "" and indentation_str == "":
indentation_str = line.replace(line.lstrip(), "")
if line.strip() == "":
continue
elif line.rstrip()[-1] == ":":
key = line.strip()[:-1]
quoteless = (
is_float(key)
or is_integer(key)
or key == "True"
or key == "False"
or ("[" in key and "]" in key)
)
if len(line.replace(line.strip(), "")) // 2 < len(levels):
if quoteless:
levels[len(line.replace(line.strip(), "")) // 2] = f"[{key}]"
else:
levels[len(line.replace(line.strip(), "")) // 2] = f"['{key}']"
else:
if quoteless:
levels.append(f"[{line.strip()[:-1]}]")
else:
levels.append(f"['{line.strip()[:-1]}']")
if quoteless:
exec(
f"data{''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}[{key}]"
+ " = {}"
)
else:
exec(
f"data{''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}['{key}']"
+ " = {}"
)
continue
key = line.split(":")[0].strip()
value = ":".join(line.split(":")[1:]).strip()
if (
is_float(value)
or is_integer(value)
or value == "True"
or value == "False"
or ("[" in value and "]" in value)
):
if (
is_float(key)
or is_integer(key)
or key == "True"
or key == "False"
or ("[" in key and "]" in key)
):
exec(
f"data{'' if line == line.strip() else ''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}[{key}] = {value}"
)
else:
exec(
f"data{'' if line == line.strip() else ''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}['{key}'] = {value}"
)
else:
if (
is_float(key)
or is_integer(key)
or key == "True"
or key == "False"
or ("[" in key and "]" in key)
):
exec(
f"data{'' if line == line.strip() else ''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}[{key}] = '{value}'"
)
else:
exec(
f"data{'' if line == line.strip() else ''.join(str(i) for i in levels[:line.replace(line.lstrip(), '').count(indentation_str) if indentation_str != '' else 0])}['{key}'] = '{value}'"
)
return data
def dumps(yaml: dict, indent="") -> str:
"""A procedure which converts the dictionary passed to the procedure into it's yaml equivalent.
Args:
yaml (dict): The dictionary to be converted.
Returns:
data (str): The dictionary in yaml form.
"""
data = ""
for key in yaml.keys():
if type(yaml[key]) == dict:
data += f"
{indent}{key}:
"
data += dumps(yaml[key], f"{indent} ")
else:
data += f"{indent}{key}: {yaml[key]}
"
return data
print(load("config.yml"))
例子
config.yml
level 0 value: 0
level 1:
level 1 value: 1
level 2:
level 2 value: 2
level 1 2:
level 1 2 value: 1 2
level 2 2:
level 2 2 value: 2 2
输出
{'level 0 value': 0, 'level 1': {'level 1 value': 1, 'level 2': {'level 2 value': 2}}, 'level 1 2': {'level 1 2 value': '1 2', 'level 2 2': {'level 2 2 value': 2 2}}}
解决方案 10:
read_yaml_file 函数将所有数据返回到字典中。
def read_yaml_file(full_path=None, relative_path=None):
if relative_path is not None:
resource_file_location_local = ProjectPaths.get_project_root_path() + relative_path
else:
resource_file_location_local = full_path
with open(resource_file_location_local, 'r') as stream:
try:
file_artifacts = yaml.safe_load(stream)
except yaml.YAMLError as exc:
print(exc)
return dict(file_artifacts.items())