PyYaml — дамп юникода со специальными символами (например, акцентами)

Я работаю с файлами yaml, которые должны быть доступны для чтения и редактирования человеком, но также будут редактироваться из кода Python. Я использую Python 2.7.3

Файл должен обрабатывать акценты (в основном для обработки текста на французском языке).

Вот пример моей проблемы:

import codecs
import yaml

file = r'toto.txt'

f = codecs.open(file,"w",encoding="utf-8")

text = u'héhéhé, hûhûhû'

textDict = {"data": text}

f.write( 'write unicode     : ' + text + '\n' )
f.write( 'write dict        : ' + unicode(textDict) + '\n' )
f.write( 'yaml dump unicode : ' + yaml.dump(text))
f.write( 'yaml dump dict    : ' + yaml.dump(textDict))
f.write( 'yaml safe unicode : ' + yaml.safe_dump(text))
f.write( 'yaml safe dict    : ' + yaml.safe_dump(textDict))

f.close()

Записанный файл содержит:

write unicode     : héhéhé, hûhûhû
write dict        : {'data': u'h\xe9h\xe9h\xe9, h\xfbh\xfbh\xfb\n'}

yaml dump unicode : "h\xE9h\xE9h\xE9, h\xFBh\xFBh\xFB"
yaml dump dict    : {data: "h\xE9h\xE9h\xE9, h\xFBh\xFBh\xFB"}

yaml safe unicode : "h\xE9h\xE9h\xE9, h\xFBh\xFBh\xFB"
yaml safe dict    : {data: "h\xE9h\xE9h\xE9, h\xFBh\xFBh\xFB"}

Дамп yaml отлично работает для загрузки с помощью yaml, но он не читается человеком.

Как вы можете видеть в коде примера, результат тот же, когда я пытаюсь написать Unicode-представление словаря (я не знаю, связано это или нет).

Я бы хотел, чтобы дамп содержал текст с акцентом, а не код юникода. Это возможно ?


person Hans Baldzuhn    schedule 30.03.2015    source источник
comment
Это Python 2, я полагаю? Я не слишком хорошо разбираюсь в обработке Unicode в Python 2, но вместо этого вы можете попробовать yaml.safe_dump, который выводит данные в формате, не зависящем от реализации, а не в формате, специфичном для Python.   -  person deceze♦    schedule 30.03.2015
comment
Ах да, извините, это Python 2.7.3, и использование safe_dump дает точно такой же результат.   -  person Hans Baldzuhn    schedule 30.03.2015


Ответы (2)


yaml может сбрасывать символы Юникода, предоставляя аргумент ключевого слова allow_unicode=True любому из дамперов. Если вы не предоставите файл, вы получите строку utf-8 обратно из метода dump() (то есть результат getvalue() в экземпляре StringIO(), созданном для хранения выгруженных данных), и вы должны преобразовать его в utf-8 перед добавлением это к вашей строке

# coding: utf-8

import codecs
import ruamel.yaml as yaml

file_name = r'toto.txt'

text = u'héhéhé, hûhûhû'

textDict = {"data": text}

with open(file_name, 'w') as fp:
    yaml.dump(textDict, stream=fp, allow_unicode=True)

print('yaml dump dict 1   : ' + open(file_name).read()),

f = codecs.open(file_name,"w",encoding="utf-8")
f.write('yaml dump dict 2   : ' + yaml.dump(textDict, allow_unicode=True).decode('utf-8'))
f.close()
print(open(file_name).read())

выход:

yaml dump dict 1    : {data: 'héhéhé, hûhûhû'}
yaml dump dict 2    : {data: 'héhéhé, hûhûhû'}

Я проверил это с моей расширенной версией PyYAML (ruamel.yaml), но это должно работать то же самое в самом PyYAML.

person Anthon    schedule 13.04.2015
comment
Большое спасибо ! Это работает отлично. Я попробовал аргумент allow_unicode, но безуспешно (мне не хватало части декодирования). - person Hans Baldzuhn; 14.04.2015
comment
Уважаемый Anthon, я не знаю, почему это решение выдает следующую ошибку: UnicodeEncodeError: кодек 'charmap' can' t кодировать символ.... Я использую Windows 10 Eng + Python 3.6 - person ragesz; 20.06.2018
comment
@ragesz Python3 уже поддерживает Unicode, если вы его используете, то не используйте codecs.open. - person Anthon; 23.06.2018
comment
Первый метод работает для меня, но он также оставляет !!python/unicode идентификаторов в YAML, когда в тексте нет символов, отличных от ASCII. Есть ли способ избавиться от них? - person Pygmalion; 08.11.2019
comment
@Pygmalion Я не уверен, чего вы пытаетесь достичь, с каким кодом, какой версией Python и на какой платформе. Результатом является фактическое выполнение кода (на Python 2.7), поэтому вы должны делать что-то другое. Пожалуйста, опубликуйте полный вопрос. - person Anthon; 08.11.2019

Обновление (2020)

В настоящее время PyYaml легко обрабатывает Unicode с помощью Python 3, но для этого требуется allow_unicode=True аргумент:

import yaml
d = {'a': 'héhéhé', 'b': 'hühühü'}
yaml_code = yaml.dump(d, allow_unicode=True, sort_keys=False)
print(yaml_code)

Приведет к:

a: héhéhé
b: hühühü

Примечание. Аргумент sortkeys=False следует использовать, начиная с Python 3.6, чтобы не изменять ключи словаря. PyYaml традиционно сортирует ключи, потому что словари Python не имеют определенного порядка. Несмотря на то, что ключи словаря упорядочиваются, начиная с Python 3.6; и официально с версии 3.7 PyYaml продолжает сортировать ключи по умолчанию.

person fralau    schedule 18.11.2020