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