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
'spam'+"eggs"+'''some'''+"""more"""
будет обработана?