Обрабатывать escape-последовательности в строке на Python
иногда, когда я получаю ввод из файла или пользователя, я получаю строку с escape-последовательностями в ней. Я хотел бы обработать escape-последовательности точно так же, как Python обрабатывает escape-последовательности в строковых литералах.
например, скажем myString определено как:
>>> myString = "spamneggs"
>>> print(myString)
spamneggs
мне нужна функция (я назову ее process) что это:
>>> print(process(myString))
spam
eggs
важно, что функция может обрабатывать все escape-последовательности в Python (перечисленный в таблице в ссылке выше).
есть ли у Python функция для этого?
7 ответов:
правильная вещь, чтобы сделать, это использовать строку-код для декодирования строки.
>>> 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. Использование строковых кодеков намного безопаснее.
unicode_escapeне работает вообщеполучается, что
string_escapeилиunicode_escapeрешение не работает вообще -- в частности, оно не работает при наличии фактического Unicode.если вы можете быть уверены в том, что каждый символ без ASCII будет экранирован (и помните, что все, что выходит за пределы первых 128 символов, не является ASCII),
unicode_escapeбудет делать правильные вещи для вас. Но если в вашей строке уже есть какие-либо литеральные символы, отличные от ASCII, все пойдет не так.
unicode_escapeпринципиально предназначен для преобразования байтов в текст Юникода. Но во многих местах - например, исходный код 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.)
The
unicode_escapeкодек, несмотря на свое название, оказывается, предполагает, что все байты, отличные от ASCII, находятся в кодировке Latin-1 (ISO-8859-1). Так что ты бы сделать это так:>>> print(s.encode('latin-1').decode('unicode_escape')) naïve testно это ужасно. Это ограничивает вас 256 латинскими символами-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
фактически правильный и удобный ответ для 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не заботится или не нужно знать о кодировке объекта byte, но кодировка экранированных байтов должна соответствовать кодировке остальной части объект.Справочная информация:
- @rspeer правильно:
unicode_escapeэто неправильное решение для python3. Это потому чтоunicode_escapeдекодирует экранированные байты, затем декодирует байты в строку unicode, но не получает никакой информации о том, какой кодек использовать для второй операции.- @Jerub правильно: избегайте AST или eval.
- я впервые обнаружил
codecs.escape_decodeС это ответ на вопрос " как я могу .декодировать ('string-escape') в Python3?". Как говорится в этом ответе, эта функция в настоящее время не документирована для python 3.
The
ast.literal_evalфункция подходит близко, но она будет ожидать, что строка будет правильно процитирована в первую очередь.конечно, интерпретация Python обратных слеш-экранирований зависит от того, как строка цитируется (
""vsr""vsu"", тройные кавычки и т. д.), Поэтому вы можете обернуть пользовательский ввод в подходящие кавычки и перейти кliteral_eval. Обертывание его в кавычки также предотвратитliteral_evalот возврата числа, кортежа, словаря и т. д.все еще может получить сложно, если пользователь вводит некотируемые кавычки типа, который вы собираетесь обернуть вокруг строки.
rspeer это правильно указывает, что
unicode-escapeвключает в себя неявное декодирование с помощьюlatin-1, но не выполнить его. Еслиunicode-escapeправильно декодирует побеги, но неправильно обрабатывает необработанные байты без ASCII, декодируя их какlatin-1, то простое исправление заключается не в том, чтобы получить регулярное выражение, а в том, чтобы повторно закодировать их какlatin-1после этого (чтобы отменить ошибочную часть процесса), затем декодировать в правильной кодировке. Например, пример неправильного использования:>>> s = 'naïve \t test' >>> print(s.encode('utf-8').decode('unicode_escape')) naïve testможно сделать тривиально правильно, добавив
.encode('latin-1').decode('utf-8'), делая это:>>> s = 'naïve \t test' >>> print(s.encode('utf-8').decode('unicode_escape').encode('latin-1').decode('utf-8')) naïve test # Or using codecs.decode to replace the first encode/decode pair with a single text->text transform: >>> print(codecs.decode(s, 'unicode_escape').encode('latin-1').decode('utf-8')) naïve testконечно, это много взад и вперед, и я бы не хотел встроить его в свой код, но его можно отнести к автономной функции, которая работает для обоих
strиbytes(с дополнительным шагом декодирования дляbytesесли результат находится в известной кодировке):def decode_escapes(s, encoding=None): if isinstance(s, str): if encoding is not None: return TypeError("Do not pass encoding for string arguments") # UTF-8 will allow correct interpretation of escapes when bytes form # interpreted as latin-1 s = s.encode('utf-8') encoding = 'utf-8' decoded = s.decode('unicode_escape').encode('latin-1') if encoding is not None: # If encoding is provided, or we started with an arbitrary string, decode decoded = decode.decode(encoding) return decoded
ниже код должен работать для \n должен отображаться в строке.
import string our_str = 'The String is \n, \n and \n!' new_str = string.replace(our_str, '/\n', '/\n', 1) print(new_str)
Если Вы доверяете источнику данных, просто пощечина кавычки вокруг него и eval() это?
>>> myString = 'spam\neggs' >>> print eval('"' + myString.replace('"','') + '"') spam eggsPS. добавлен счетчик evil-code-exec-теперь он будет лишать всех
"прежде чем использовать eval-ить
Comments