Mam ten przykładowy plik yaml:

---
test:
  name: "Tom"
  age: "5"
  version: "1.0"

Jak mogę wymienić ten plik yaml do tego:


test:
  name: "Max"
  age: "10"
  version: "2.2"

W ten sposób otwieram plik:

import yaml

with open("config.yml", 'r') as stream:
        print(yaml.load(stream))

Ale nie mam pojęcia, jak edytować plik YAML teraz.

1
L.Kersting 21 luty 2019, 12:25

2 odpowiedzi

Najlepsza odpowiedź

Biorąc pod uwagę fakt, że używasz PyYaml, właściwy sposób na to jest taki:

#!/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)

Więc po prostu załadujesz Yaml do dict Nazywany loaded, modyfikujesz wartości potrzebne, a następnie zapisujesz go (nadpisanie oryginalnego pliku lub nie, połączenie). Dla zagnieżdżonego wejścia miałbyś zagnieżdżony dykt, że będziesz musiał zmodyfikować. Parametr default_flow_style=False jest niezbędny do wytworzenia żądanego formatu (styl przepływu), w przeciwnym razie zagnieżdżone kolekcje produkuje styl blokowy:

A: a
B: {C: c, D: d, E: e}

Twoje zdrowie!

Późniejsza edycja :

Ponieważ anthon wskazał, moja odpowiedź ma kilka wad.

  • Lepiej używać safe_load zamiast load, ponieważ później jest potencjalnie niebezpieczny.

  • Wyjście wymaga wskaźnika końca dyrektywy (te trzy myślniki na początku). Aby je dołączyć, używamy explicit_start=True w metodzie dump (co powinno być safe_dump).

  • Użyj może ruamel.yaml zamiast yaml, jeśli chcesz wygenerować lepszą wydajność (chociaż są semantycznie tak samo)

Zobacz odpowiedź Anthon, aby uzyskać bardziej szczegółowe informacje, ponieważ jest autorem pakietu.

4
Adrian Pop 21 luty 2019, 10:47

Jeśli przeczytasz dokumentację Pyyaml, zobaczysz, że to mówi, że przy użyciu funkcji {X0}} jest potencjalnie niebezpieczna, Więc pierwsza rzecz do zrobienia (od ciebie i prawie wszyscy inni nie Potrzebujesz tego), nie używa tego, ale zamiast tego używa safe_load().

Powinieneś także zmienić plik wejściowy na config.yaml, The Zalecane rozszerzenie dla plików YAML zostało .yaml Od momentu 2006 roku.

Wiedząc, że sposób zmiany pliku config.yaml za pomocą Pyyaml:

import yaml

with open('config.yaml') as stream:
   data = yaml.safe_load(stream)

test = data['test']
test.update(dict(name="Tom", age="10", version="2.2"))

with open('output.yaml', 'wb') as stream:
   yaml.safe_dump(data, stream, default_flow_style=False, 
                  explicit_start=True, allow_unicode=True, encoding='utf-8')

To dostanie ci output.yaml, który wygląda:

---
test:
  age: '10'
  name: Tom
  version: '2.2'

Parametr default_flow_style jest niezbędny, aby nie uzyskać jsona Struktura mapowania węzła liściowego. explicit_start, aby uzyskać wiodący wskaźnik końcowy dyrezy ({x2}}) i zalecam zawsze użycie allow_unicode=True, encoding='utf-8' (i otwórz plik jako binarny) Aby nie biegać w niespodzianki lub problemy, gdy zmienisz name do Björk Guðmundsdóttir.

Teraz, kiedy zauważysz, nie generuje to żądanego wyjścia (choć semantycznie tego samego):

  • pojedyncze cytaty zamiast podwójnych cytatów wokół strun, które mogą być interpretowane jako liczby
  • Brak podwójnych cytatów wokół Tom
  • Sortowanie kluczy mapowania

Jeśli miałeś komentarze do pliku YAML, zostałyby to utracone.

Lepszy sposób na aktualizację plików YAML używa ruamel.yaml (Zastrzeżenie: Jestem autorem tego pakietu), który ma więcej Sany domyślnie niż pyyaml, uchwyty Yaml 1.2 i nie upuszczają komentarzy (Jeśli masz je w swoim pliku):

import ruamel.yaml


yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
yaml.explicit_start = True

with open('config.yaml') as stream:
   data = yaml.load(stream)

test = data['test']
test.update(dict(name="Tom", age="10", version="2.2"))

with open('output.yaml', 'wb') as stream:
    yaml.dump(data, stream)

Z tym plikiem wyjściowym będzie:

---
test:
  name: "Tom"
  age: "10"
  version: "2.2"

Który jest dokładnie tym, czego chciałeś.

2
Anthon 21 luty 2019, 10:18