Обработка escape-последовательностей в строке в Python - PullRequest
87 голосов
/ 26 октября 2010

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

Например, скажем, myString определяется как:

>>> myString = "spam\\neggs"
>>> print(myString)
spam\neggs

Мне нужна функция (я назову ее process), которая делает это:

>>> print(process(myString))
spam
eggs

Важно, чтобы функция могла обрабатывать все escape-последовательности в Python (перечислены в таблице).по ссылке выше).

Есть ли в Python функция для этого?

Ответы [ 6 ]

116 голосов
/ 26 октября 2010

Правильнее всего использовать код 'string-escape' для декодирования строки.

>>> myString = "spam\\neggs"
>>> decoded_string = bytes(myString, "utf-8").decode("unicode_escape") # python3 
>>> decoded_string = myString.decode('string_escape') # python2
>>> print(decoded_string)
spam
eggs

Не используйте AST или eval.Использование строковых кодеков намного безопаснее.

100 голосов
/ 02 июля 2014

unicode_escape не работает вообще

Оказывается, что решение string_escape или 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 заменить их на их неэкранированное значение.

import re
import codecs

ESCAPE_SEQUENCE_RE = re.compile(r'''
    ( \\U........      # 8-digit hex escapes
    | \\u....          # 4-digit hex escapes
    | \\x..            # 2-digit hex escapes
    | \\[0-7]{1,3}     # Octal escapes
    | \\N\{[^}]+\}     # Unicode characters by name
    | \\[\\'"abfnrtv]  # Single-character escapes
    )''', re.UNICODE | re.VERBOSE)

def decode_escapes(s):
    def decode_match(match):
        return codecs.decode(match.group(0), 'unicode-escape')

    return ESCAPE_SEQUENCE_RE.sub(decode_match, s)

И с этим:

>>> print(decode_escapes('Ernő \\t Rubik'))
Ernő     Rubik
22 голосов
/ 05 мая 2016

Действительно правильный и удобный ответ для python 3:

>>> import codecs
>>> myString = "spam\\neggs"
>>> print(codecs.escape_decode(bytes(myString, "utf-8"))[0].decode("utf-8"))
spam
eggs
>>> myString = "naïve \\t test"
>>> print(codecs.escape_decode(bytes(myString, "utf-8"))[0].decode("utf-8"))
naïve    test

Подробности относительно codecs.escape_decode:

  • codecs.escape_decode - это байтовый байтовый декодер
  • codecs.escape_decode декодирует escape-последовательности ascii, такие как: b"\\n" -> b"\n", b"\\xce" -> b"\xce".
  • codecs.escape_decode не заботится или не должен знать окодировка байтового объекта, но кодировка экранированных байтов должна соответствовать кодировке остальной части объекта.

Фон:

  • @ rspeer правильно: unicode_escape - неправильное решение для python3.Это связано с тем, что unicode_escape декодирует экранированные байты, затем декодирует байты в строку Unicode, но не получает информации о том, какой кодек использовать для второй операции.AST или eval.
  • Я впервые обнаружил codecs.escape_decode из этого ответа на вопрос "как мне .decode ('string-escape') в Python3?" .Как говорится в этом ответе, эта функция в настоящее время не документирована для Python 3.
8 голосов
/ 26 октября 2010

Функция ast.literal_eval подходит близко, но она будет ожидать, что строка будет правильно заключена в кавычки.

Конечно, интерпретация Python экранирования обратной косой черты зависит от того, как строка заключена в кавычки ("" против r"" против u"", тройные кавычки и т. Д.), Поэтому вы можете захотеть заключить пользовательский ввод в соответствующие кавычки и перейти literal_eval. Заключение в кавычки также не позволит literal_eval вернуть число, кортеж, словарь и т. Д.

Вещи все еще могут быть хитрыми, если пользователь вводит кавычки без кавычек того типа, который вы намереваетесь обернуть вокруг строки.

2 голосов
/ 05 марта 2019

Это плохой способ сделать это, но он сработал для меня, когда я пытался интерпретировать экранированные восьмеричные числа, переданные в строковом аргументе.

input_string = eval('b"' + sys.argv[1] + '"')

Стоит отметить, что между eval и ast есть разница.literal_eval (eval более небезопасен).См. Использование функции python eval () и ast.literal_eval ()?

1 голос
/ 26 марта 2018

Ниже код должен работать для \ n должен отображаться в строке.

import string

our_str = 'The String is \\n, \\n and \\n!'
new_str = string.replace(our_str, '/\\n', '/\n', 1)
print(new_str)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...