Не используйте AST или eval. Использование строковых кодеков намного безопаснее.
Ответ 2
unicode_escape вообще не работает
Оказывается, что решение string_escape or unicode_escape не работает в целом - в частности, оно не работает при наличии фактического Unicode.
Если вы можете быть уверены, что каждый символ, отличный от ASCII, будет экранирован (и помните, что все, что находится за пределами первых 128 символов, не является ASCII), unicode_escape будет сделано правильно для вас. Но если в вашей строке уже есть какие-либо буквенные символы, отличные от ASCII, все пойдет не так.
unicode_escape в основе своей предназначен для преобразования байтов в текст в формате Unicode. Но во многих местах - например, в исходном коде Python - исходными данными уже является текст в формате Unicode.
Единственный способ, которым это может работать правильно, - это если вы сначала закодируете текст в байты. UTF-8 - это разумная кодировка для всего текста, так что это должно сработать, верно?
Следующие примеры приведены в Python 3, так что строковые литералы более чистые, но та же проблема существует с немного разными проявлениями как в Python 2, так и в 3.
>>> s = 'naïve \\t test' >>> print(s.encode('utf-8').decode('unicode_escape')) naïve test
Ну, это неправильно.
Новый рекомендуемый способ использования кодеков, которые декодируют текст в текст, - это прямой вызов codecs.decode. Помогает ли это?
>>> import codecs >>> print(codecs.decode(s, 'unicode_escape')) naïve test
Вовсе нет. (Кроме того, приведенное выше является ошибкой UnicodeError в Python 2.)
В unicode_escape кодеке, несмотря на его название, предполагается, что все байты, отличные от ASCII, находятся в кодировке Latin-1 (ISO-8859-1). Итак, вам придется сделать это следующим образом:
>>> print(s.encode('latin-1').decode('unicode_escape')) naïve test
Но это ужасно. Это ограничивает вас 256 символами Latin-1, как будто Unicode вообще никогда не изобретали!
>>> print('Ernő \\t Rubik'.encode('latin-1').decode('unicode_escape')) UnicodeEncodeError: 'latin-1' codec can't encode character '\u0151' in position 3: ordinal not in range(256)
Добавление регулярного выражения для решения проблемы
(Удивительно, но теперь у нас действительно есть две проблемы.)
Что нам нужно сделать, так это применить unicode_escape декодер только к тому, что мы уверены, что это текст ASCII. В частности, мы можем убедиться, что применяем это только к допустимым escape-последовательностям Python, которые гарантированно будут текстом ASCII.
План таков: мы найдем escape-последовательности, используя регулярное выражение, и используем функцию в качестве аргумента для re.sub, чтобы заменить их неэкранированным значением.
codecs.escape_decode не заботится и не нуждается в знании о кодировке объекта byte, но кодировка экранированных байтов должна соответствовать кодировке остальной части объекта.
Справочная информация:
@rspeer правильно: unicode_escape это неправильное решение для python3. Это происходит потому, что unicode_escape декодирует экранированные байты, затем декодирует байты в строку Unicode, но не получает информации о том, какой кодек использовать для второй операции.
Функция ast.literal_eval подходит близко, но сначала она ожидает, что строка будет заключена в правильные кавычки.
Конечно, интерпретация экранирования обратной косой черты в Python зависит от того, как строка заключена в кавычки ("" vs r"" против u"", тройные кавычки и т.д.), Поэтому вы можете захотеть заключить пользовательский ввод в подходящие кавычки и передать в literal_eval. Заключать ее в кавычки также не позволит literal_eval возвращать число, кортеж, словарь и т.д.
Все еще может быть сложно, если пользователь вводит кавычки без кавычек того типа, который вы собираетесь заключить в строку.