web-dev-qa-db-fra.com

comment puis-je décoder ('string-escape') en Python3?

J'ai des chaînes échappées qui doivent être échappées. Je voudrais le faire en Python.

Par exemple, en python2.7, je peux faire ceci:

>>> "\\123omething special".decode('string-escape')
'Something special'
>>> 

Comment faire en Python3? Cela ne fonctionne pas:

>>> b"\\123omething special".decode('string-escape')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
LookupError: unknown encoding: string-escape
>>> 

Mon objectif est d'être capable de prendre une chaîne comme celle-ci:

s\000u\000p\000p\000o\000r\000t\000@\000p\000s\000i\000l\000o\000c\000.\000c\000o\000m\000

Et transformez-le en:

"[email protected]"

Après avoir effectué la conversion, je vais vérifier si la chaîne que j'ai est encodée en UTF-8 ou UTF-16.

46
vy32

Vous devrez utiliser unicode_escape À la place:

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

Si vous démarrez avec un objet str à la place (équivalent à python 2.7 unicode), vous devrez d'abord coder en octets, puis décoder avec unicode_escape.

Si vous avez besoin d'octets comme résultat final, vous devrez recoder à un codage approprié (.encode('latin1') par exemple, si vous avez besoin de conserver les valeurs d'octets littérales; les 256 premiers points de code Unicode mappent 1-sur- 1).

Votre exemple est en fait des données UTF-16 avec des échappements. Décodez à partir de unicode_escape, Retournez à latin1 Pour conserver les octets, puis à partir de utf-16-le (UTF 16 petit endian sans BOM):

>>> 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
'[email protected]'
47
Martijn Pieters

L'ancien codec "string-escape" mappe les bytestrings aux bytestrings, et il y a eu beaucoup de débats sur ce qu'il faut faire avec de tels codecs, donc il n'est pas actuellement disponible via les interfaces de codage/décodage standard.

MAIS, le code est toujours là dans l'API C (comme PyBytes_En/DecodeEscape), et cela est toujours exposé à Python via le codecs.escape_encode et 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)

Ces fonctions renvoient l'objet bytes transformé, plus un nombre indiquant le nombre d'octets traités ... vous pouvez simplement ignorer ce dernier.

>>> 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'
24
Nathaniel J. Smith

Vous ne pouvez pas utiliser unicode_escape sur des chaînes d'octets (ou plutôt, vous pouvez, mais cela ne renvoie pas toujours la même chose que string_escape le fait Python 2) - attention!

Cette fonction implémente string_escape à l'aide d'une expression régulière et d'une logique de remplacement personnalisée.

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)
3
malthe

Au moins dans mon cas, c'était équivalent:

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