Как я могу разобрать файл YAML в Python
Как я могу разобрать файл YAML в Python?
12 ответов
Самый простой и чистый метод без использования заголовков C - это PyYaml:
#!/usr/bin/env python
import yaml
with open("example.yaml", 'r') as stream:
try:
print(yaml.load(stream))
except yaml.YAMLError as exc:
print(exc)
И это все. Больше информации здесь:
Чтение и запись файлов YAML с Python 2+3 (и Unicode)
# -*- 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.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 ( чтение и запись)
- MessagePack ( пакет Python): более компактное представление ( чтение и запись)
- HDF5 ( пакет Python): отлично подходит для матриц ( чтение и запись)
- XML: существует тоже * вздох * ( чтение и запись)
Для вашего приложения может быть важно следующее:
- Поддержка другими языками программирования
- Чтение / запись производительности
- Компактность (размер файла)
Смотрите также: Сравнение форматов сериализации данных
Если вы предпочитаете создавать конфигурационные файлы, вы можете прочитать мою короткую статью Конфигурационные файлы в Python
Если у вас есть YAML, который соответствует спецификации YAML 1.2 (выпущен в 2009 году), вам следует использовать 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()
если вы действительно не имеете полного контроля над вводом данных, он нужен (редко бывает) и знает, что вы делаете.
Если вы используете pathlib Path
для манипулирования файлами лучше использовать новый API ruamel.yaml:
from ruamel.yaml import YAML
from pathlib import Path
path = Path('example.yaml')
yaml = YAML(typ='safe')
data = yaml.load(path)
Импортируйте модуль yaml и загрузите файл в словарь под названием "my_dict":
import yaml
my_dict = yaml.load(open('filename'))
Это все, что вам нужно. Теперь весь файл yaml находится в словаре my_dict.
Чтобы получить доступ к любому элементу списка в файле 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'])
Пример:
defaults.yaml
url: https://www.google.com
environment.py
from ruamel import yaml
data = yaml.safe_load(open('defaults.yaml'))
data['url']
Я использую 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 не мертв, как оказалось, он просто поддерживается в другом месте.
Я сделал свой собственный сценарий для этого. Не стесняйтесь использовать его, пока вы сохраняете атрибуцию. Скрипт умеет парсить 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("\n")
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("\n"):
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"\n{indent}{key}:\n"
data += dumps(yaml[key], f"{indent} ")
else:
data += f"{indent}{key}: {yaml[key]}\n"
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}}}
#!/usr/bin/env python
import sys
import yaml
def main(argv):
with open(argv[0]) as stream:
try:
#print(yaml.load(stream))
return 0
except yaml.YAMLError as exc:
print(exc)
return 1
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
Учитывая вышеупомянутые ответы, все из которых хороши, существует пакет Python, доступный для интеллектуального создания объектов из YAML / JSON / dicts, который активно разрабатывается и расширяется. (полное раскрытие, я являюсь соавтором этого пакета , см. здесь)
Установить:
pip install pickle-rick
Использовать:
Определите строку (или файл) YAML или JSON.
BASIC:
text: test
dictionary:
one: 1
two: 2
number: 2
list:
- one
- two
- four
- name: John
age: 20
USERNAME:
type: env
load: USERNAME
callable_lambda:
type: lambda
load: "lambda: print('hell world!')"
datenow:
type: lambda
import:
- "from datetime import datetime as dd"
load: "lambda: print(dd.utcnow().strftime('%Y-%m-%d'))"
test_function:
type: function
name: test_function
args:
x: 7
y: null
s: hello world
any:
- 1
- hello
import:
- "math"
load: >
def test(x, y, s, any):
print(math.e)
iii = 111
print(iii)
print(x,s)
if y:
print(type(y))
else:
print(y)
for i in any:
print(i)
Затем используйте его как объект.
>> from pickle_rick import PickleRick
>> config = PickleRick('./config.yaml', deep=True, load_lambda=True)
>> config.BASIC.dictionary
{'one' : 1, 'two' : 2}
>> config.BASIC.callable_lambda()
hell world!
Вы можете определять функции Python, загружать дополнительные данные из других файлов или REST API, переменных среды, а затем снова записывать все в YAML или JSON.
Это особенно хорошо работает при создании систем, требующих структурированных файлов конфигурации, или в записных книжках в виде интерактивных структур.
Это примечание по безопасности. Загружайте только те файлы, которым вы доверяете, так как любой код может быть выполнен, поэтому избегайте простой загрузки чего-либо, не зная, каково полное содержимое.
Пакет называется PickleRick и доступен здесь:
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())
Предложение: используйте yq (доступно через pip)
Я не уверен, как это не было предложено раньше, но я настоятельно рекомендую использовать yq , который является оболочкой jq для YAML.
yq использует синтаксис, подобный jq, но работает с файлами yaml так же, как и с json.
Примеры:
1 ) Прочитайте значение:
yq e '.a.b[0].c' file.yaml
2 ) Труба из STDIN:
cat file.yaml | yq e '.a.b[0].c' -
3) Обновите файл yaml на месте
yq e -i '.a.b[0].c = "cool"' file.yaml
4) Обновление с использованием переменных среды:
NAME=mike yq e -i '.a.b[0].c = strenv(NAME)' file.yaml
5) Объединить несколько файлов:
yq ea '. as $item ireduce ({}; . * $item )' path/to/*.yml
6) Несколько обновлений файла yaml:
yq e -i '
.a.b[0].c = "cool" |
.x.y.z = "foobar" |
.person.name = strenv(NAME)
' file.yaml
(*) Подробнее о том, как парсить поля из yaml с помощью jq-фильтров .