Как я .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\[email protected]\000p\000s\000i\000l\000o\000c\000.\000c\000o\000m\000

И превратить это в:

"[email protected]"

После того, как я выполню преобразование, я проверю, зашифрована ли моя строка в UTF-8 или UTF-16.

Ответ 1

unicode_escape этого вам придется использовать unicode_escape:

>>> b"\\123omething special".decode('unicode_escape')

Если вместо этого вы начинаете с объекта str (эквивалентного unicode_escape python 2.7), вам сначала нужно будет кодировать в байты, а затем декодировать с помощью unicode_escape.

Если вам нужен байт в качестве конечного результата, вам придется снова кодировать в подходящую кодировку (например, .encode('latin1'), если вам нужно сохранить буквенные байтовые значения; первые 256 кодовых точек Unicode отображаются 1-на-1). 1).

Ваш пример на самом деле данные UTF-16 с escape-кодами. unicode_escape из unicode_escape, обратно в latin1 для сохранения байтов, затем из utf-16-le (UTF 16 с unicode_escape байтов без BOM):

>>> value = b's\\000u\\000p\\000p\\000o\\000r\\000t\\[email protected]\\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\[email protected]\x00p\x00s\x00i\x00l\x00o\x00c\x00.\x00c\x00o\x00m\x00'
>>> _.decode('utf-16-le') # decode from UTF-16-LE
'[email protected]'

Ответ 2

В старых кодеках кодеков "строка-побег" происходит переключение на байты, и было много споров о том, что делать с такими кодеками, поэтому в настоящее время он недоступен через стандартные интерфейсы кодирования/декодирования.

НО, код все еще присутствует в 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\\[email protected]\\000p\\000s\\000i\\000l\\000o\\000c\\000.\\000c\\000o\\000m\\000'
>>> codecs.escape_decode(value)[0]
b's\x00u\x00p\x00p\x00o\x00r\x00t\[email protected]\x00p\x00s\x00i\x00l\x00o\x00c\x00.\x00c\x00o\x00m\x00'

Ответ 3

Вы не можете использовать 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)

Ответ 4

У меня была попытка, но она поддерживает только конкретные случаи, подобные этому.

import re
w=str(["中文".encode(),"测试".encode()])
print(w)

def string_escape(gstr):
    res = re.findall(r"(b'.*?')", gstr)
    for r in res:
        gstr = gstr.replace(r, repr(eval(r).decode()))
    return gstr

print(string_escape(w))

Ответ 5

По крайней мере, в моем случае это было эквивалентно:

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')