Я хотел бы иметь возможность выгружать словарь, содержащий длинные строки, которые я хотел бы иметь в стиле блоков для удобочитаемости. Например:
PyYAML поддерживает загрузку документов с этим стилем, но я не могу найти способ сбрасывать документы таким образом. Я что-то пропустил?
Ответ 1
import yaml
class folded_unicode(unicode): pass
class literal_unicode(unicode): pass
def folded_unicode_representer(dumper, data):
return dumper.represent_scalar(u'tag:yaml.org,2002:str', data, style='>')
def literal_unicode_representer(dumper, data):
return dumper.represent_scalar(u'tag:yaml.org,2002:str', data, style='|')
yaml.add_representer(folded_unicode, folded_unicode_representer)
yaml.add_representer(literal_unicode, literal_unicode_representer)
data = {
'literal':literal_unicode(
u'by hjw ___\n'
' __ /.-.\\\n'
' / )_____________\\\\ Y\n'
' /_ /=== == === === =\\ _\\_\n'
'( /)=== == === === == Y \\\n'
' `-------------------( o )\n'
' \\___/\n'),
'folded': folded_unicode(
u'It removes all ordinary curses from all equipped items. '
'Heavy or permanent curses are unaffected.\n')}
print yaml.dump(data)
Результат:
folded: >
It removes all ordinary curses from all equipped items. Heavy or permanent curses
are unaffected.
literal: |
by hjw ___
__ /.-.\
/ )_____________\\ Y
/_ /=== == === === =\ _\_
( /)=== == === === == Y \
`-------------------( o )
\___/
Для полноты, также должны быть строковые реализации, но я буду ленив: -)
Ответ 2
pyyaml
поддерживает сброс литеральных или сложенных блоков.
Использование Representer.add_representer
определяющие типы:
class folded_str(str): pass
class literal_str(str): pass
class folded_unicode(unicode): pass
class literal_unicode(str): pass
Затем вы можете определить представителей для этих типов.
Обратите внимание, что в то время как Gary решение отлично работает для юникода, вам может понадобиться еще одна работа для получения строк для правильной работы (см. реализация submit_str).
def change_style(style, representer):
def new_representer(dumper, data):
scalar = representer(dumper, data)
scalar.style = style
return scalar
return new_representer
import yaml
from yaml.representer import SafeRepresenter
# represent_str does handle some corner cases, so use that
# instead of calling represent_scalar directly
represent_folded_str = change_style('>', SafeRepresenter.represent_str)
represent_literal_str = change_style('|', SafeRepresenter.represent_str)
represent_folded_unicode = change_style('>', SafeRepresenter.represent_unicode)
represent_literal_unicode = change_style('|', SafeRepresenter.represent_unicode)
Затем вы можете добавить эти представления к самозагрузчику по умолчанию:
yaml.add_representer(folded_str, represent_folded_str)
yaml.add_representer(literal_str, represent_literal_str)
yaml.add_representer(folded_unicode, represent_folded_unicode)
yaml.add_representer(literal_unicode, represent_literal_unicode)
... и протестируйте его:
data = {
'foo': literal_str('this is a\nblock literal'),
'bar': folded_unicode('this is a folded block'),
}
print yaml.dump(data)
результат:
bar: >-
this is a folded block
foo: |-
this is a
block literal
Использование default_style
Если вы заинтересованы в том, чтобы все ваши строки соответствовали стилю по умолчанию, вы также можете использовать аргумент ключевого слова default_style
, например:
>>> data = { 'foo': 'line1\nline2\nline3' }
>>> print yaml.dump(data, default_style='|')
"foo": |-
line1
line2
line3
или для сложенных литералов:
>>> print yaml.dump(data, default_style='>')
"foo": >-
line1
line2
line3
или для литералов с двойными кавычками:
>>> print yaml.dump(data, default_style='"')
"foo": "line1\nline2\nline3"
Предостережение:
Вот пример чего-то, чего вы не ожидаете:
data = {
'foo': literal_str('this is a\nblock literal'),
'bar': folded_unicode('this is a folded block'),
'non-printable': literal_unicode('this has a \t tab in it'),
'leading': literal_unicode(' with leading white spaces'),
'trailing': literal_unicode('with trailing white spaces '),
}
print yaml.dump(data)
приводит к:
bar: >-
this is a folded block
foo: |-
this is a
block literal
leading: |2-
with leading white spaces
non-printable: "this has a \t tab in it"
trailing: "with trailing white spaces "
1) непечатаемые символы
См. спецификацию YAML для экранированных символов (Раздел 5.7):
Обратите внимание, что escape-последовательности интерпретируются только в двухкасканных скалярах. Во всех других скалярных стилях символ "\" не имеет особого значения, а непечатаемые символы недоступны.
Если вы хотите сохранить непечатаемые символы (например, TAB), вам нужно использовать сканеры с двойными кавычками. Если вы можете сбросить скаляр с литеральным стилем, и там есть непечатаемый символ (например, TAB), ваш самозагружатель YAML несовместим.
например. pyyaml
обнаруживает непечатаемый символ \t
и использует стиль с двойным кавычком, даже если указан стиль по умолчанию:
>>> data = { 'foo': 'line1\nline2\n\tline3' }
>>> print yaml.dump(data, default_style='"')
"foo": "line1\nline2\n\tline3"
>>> print yaml.dump(data, default_style='>')
"foo": "line1\nline2\n\tline3"
>>> print yaml.dump(data, default_style='|')
"foo": "line1\nline2\n\tline3"
2) передние и конечные пробелы
Еще одна полезная информация в спецификации:
Все символы верхнего и нижнего пробелов исключаются из содержимого
Это означает, что если ваша строка имеет начальное или конечное пустое пространство, они не будут сохраняться в скалярных стилях, отличных от двойных. Как следствие, pyyaml
пытается обнаружить то, что находится в вашем скаляре, и может заставить стиль с двойным кавычком.