Как мне.decode('string-escape') в Python3?
У меня есть некоторые экранированные строки, которые нужно убрать. Я хотел бы сделать это на Python.
Например, в python2.7 я могу сделать это:
>>> "\\123omething special".decode('string-escape')
'Something special'
>>>
Как мне это сделать в Python3? Это не работает:
>>> b"\\123omething special".decode('string-escape')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
LookupError: unknown encoding: string-escape
>>>
Моя цель состоит в том, чтобы быть абелем, чтобы взять такую строку:
s\000u\000p\000p\000o\000r\000t\000@\000p\000s\000i\000l\000o\000c\000.\000c\000o\000m\000
И превратить это в:
"support@psiloc.com"
После того, как я выполню преобразование, я проверю, зашифрована ли моя строка в UTF-8 или UTF-16.
6 ответов
Вам придется использовать unicode_escape
вместо:
>>> b"\\123omething special".decode('unicode_escape')
Если вы начнете с str
вместо объекта (эквивалентного юникоду python 2.7) вам нужно сначала кодировать в байты, а затем декодировать с помощью unicode_escape
,
Если вам нужны байты в качестве конечного результата, вам придется снова кодировать в подходящую кодировку (.encode('latin1')
например, если вам нужно сохранить буквенные байтовые значения; первые 256 кодовых точек Unicode отображаются 1-на-1).
Ваш пример - это данные UTF-16 с escape-кодами. Декодировать из unicode_escape
, вернуться к latin1
чтобы сохранить байты, то от utf-16-le
(UTF 16 little endian без спецификации):
>>> value = b's\\000u\\000p\\000p\\000o\\000r\\000t\\000@\\000p\\000s\\000i\\000l\\000o\\000c\\000.\\000c\\000o\\000m\\000'
>>> value.decode('unicode_escape').encode('latin1') # convert to bytes
b's\x00u\x00p\x00p\x00o\x00r\x00t\x00@\x00p\x00s\x00i\x00l\x00o\x00c\x00.\x00c\x00o\x00m\x00'
>>> _.decode('utf-16-le') # decode from UTF-16-LE
'support@psiloc.com'
Старый кодек "string-escape" преобразует байтовые строки в байтовые строки, и было много споров о том, что делать с такими кодеками, поэтому в настоящее время он недоступен через стандартные интерфейсы кодирования / декодирования.
НО, код все еще там в C-API (как PyBytes_En/DecodeEscape
), и это все еще доступно Python через недокументированное codecs.escape_encode
а также codecs.escape_decode
,
>>> import codecs
>>> codecs.escape_decode(b"ab\\xff")
(b'ab\xff', 6)
>>> codecs.escape_encode(b"ab\xff")
(b'ab\\xff', 3)
Эти функции возвращают преобразованный bytes
объект, плюс число, указывающее, сколько байтов было обработано... вы можете просто игнорировать последний.
>>> value = b's\\000u\\000p\\000p\\000o\\000r\\000t\\000@\\000p\\000s\\000i\\000l\\000o\\000c\\000.\\000c\\000o\\000m\\000'
>>> codecs.escape_decode(value)[0]
b's\x00u\x00p\x00p\x00o\x00r\x00t\x00@\x00p\x00s\x00i\x00l\x00o\x00c\x00.\x00c\x00o\x00m\x00'
Если вы хотите, чтобы str-to-str декодировала escape-последовательности, чтобы и ввод, и вывод были Unicode:
def string_escape(s, encoding='utf-8'):
return (s.encode('latin1') # To bytes, required by 'unicode-escape'
.decode('unicode-escape') # Perform the actual octal-escaping decode
.encode('latin1') # 1:1 mapping back to bytes
.decode(encoding)) # Decode original encoding
Тестирование:
>>> string_escape('\\123omething special')
'Something special'
>>> string_escape(r's\000u\000p\000p\000o\000r\000t\000@'
r'\000p\000s\000i\000l\000o\000c\000.\000c\000o\000m\000',
'utf-16-le')
'support@psiloc.com'
py2
"\\123omething special".decode('string-escape')
py3
"\\123omething special".encode('utf-8').decode('unicode-escape')
Вы не можете использовать unicode_escape
на байтовых строках (или, скорее, вы можете, но это не всегда возвращает то же самое, что string_escape
делает на Python 2) - будьте осторожны!
Эта функция реализует string_escape
используя регулярное выражение и пользовательскую логику замены.
def unescape(text):
regex = re.compile(b'\\\\(\\\\|[0-7]{1,3}|x.[0-9a-f]?|[\'"abfnrt]|.|$)')
def replace(m):
b = m.group(1)
if len(b) == 0:
raise ValueError("Invalid character escape: '\\'.")
i = b[0]
if i == 120:
v = int(b[1:], 16)
elif 48 <= i <= 55:
v = int(b, 8)
elif i == 34: return b'"'
elif i == 39: return b"'"
elif i == 92: return b'\\'
elif i == 97: return b'\a'
elif i == 98: return b'\b'
elif i == 102: return b'\f'
elif i == 110: return b'\n'
elif i == 114: return b'\r'
elif i == 116: return b'\t'
else:
s = b.decode('ascii')
raise UnicodeDecodeError(
'stringescape', text, m.start(), m.end(), "Invalid escape: %r" % s
)
return bytes((v, ))
result = regex.sub(replace, text)
По крайней мере, в моем случае это было эквивалентно:
Py2: my_input.decode('string_escape')
Py3: bytes(my_input.decode('unicode_escape'), 'latin1')
convertutils.py:
def string_escape(my_bytes):
return bytes(my_bytes.decode('unicode_escape'), 'latin1')