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, находятся в латинском-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