online - python 2 to python 3
_Exact_ equivalente de `b ''...''.decode(" utf-8 "," backslashreplace ")` en Python 2 (2)
Puede escribir su propio controlador de errores. Aquí hay una solución que probé en Python 2.7, 3.3 y 3.6:
from __future__ import print_function
import codecs
import sys
print(sys.version)
def myreplace(ex):
# The error handler receives the UnicodeDecodeError, which contains arguments of the
# string and start/end indexes of the bad portion.
bstr,start,end = ex.object,ex.start,ex.end
# The return value is a tuple of Unicode string and the index to continue conversion.
# Note: iterating byte strings returns int on 3.x but str on 2.x
return u''''.join(''//x{:02x}''.format(c if isinstance(c,int) else ord(c))
for c in bstr[start:end]),end
codecs.register_error(''myreplace'',myreplace)
print(b''/xc2/xa1/xa1ABC''.decode("utf-8", "myreplace"))
Salida:
C:/>py -2.7 test.py 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:42:59) [MSC v.1500 32 bit (Intel)] ¡/xa1ABC C:/>py -3.3 test.py 3.3.5 (v3.3.5:62cf4e77f785, Mar 9 2014, 10:35:05) [MSC v.1600 64 bit (AMD64)] ¡/xa1ABC C:/>py -3.6 test.py 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] ¡/xa1ABC
En Python .decode("utf-8", "backslashreplace")
es una opción bastante buena para tratar con cadenas binarias parcialmente codificadas en Unicode, parcialmente desconocidas. Las secuencias UTF-8 válidas se decodificarán y las no válidas se conservarán como secuencias de escape. Por ejemplo
>>> print(b''/xc2/xa1/xa1''.decode("utf-8", "backslashreplace"))
¡/xa1
Esto pierde la distinción entre b''/xc2/xa1/xa1''
y b''/xc2/xa1//xa1''
, pero si estás en "solo b''/xc2/xa1//xa1''
algo no demasiado desagradable que pueda arreglar a mano más tarde" estado de ánimo, eso probablemente esté bien.
Sin embargo, esta es una nueva característica en Python 3.5. El programa en el que estoy trabajando también necesita soportar 3.4 y 2.7. En esas versiones, arroja una excepción:
>>> print(b''/xc2/xa1/xa1''.decode("utf-8", "backslashreplace"))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/encodings/utf_8.py", line 16, in decode
return codecs.utf_8_decode(input, errors, True)
TypeError: don''t know how to handle UnicodeDecodeError in error callback
He encontrado una aproximación, pero no un equivalente exacto:
>>> print(b''/xc2/xa1/xa1''.decode("latin1")
... .encode("ascii", "backslashreplace").decode("ascii"))
/xc2/xa1/xa1
Es muy importante que el comportamiento no dependa de la versión del intérprete. ¿Alguien puede aconsejar una forma de obtener exactamente el comportamiento de Python 3.5 en 2.7 y 3.4?
(Las versiones anteriores de 2.x o 3.x no necesitan funcionar. Los codecs
parche de mono son totalmente aceptables).
Intenté un respaldo más completo de la implementación de cpython
Esto maneja tanto UnicodeDecodeError
(desde .decode()
) como UnicodeEncodeError
desde .encode()
y UnicodeTranslateError
desde .translate()
:
from __future__ import unicode_literals
import codecs
def _bytes_repr(c):
"""py2: bytes, py3: int"""
if not isinstance(c, int):
c = ord(c)
return ''//x{:x}''.format(c)
def _text_repr(c):
d = ord(c)
if d >= 0x10000:
return ''//U{:08x}''.format(d)
else:
return ''//u{:04x}''.format(d)
def backslashescape_backport(ex):
s, start, end = ex.object, ex.start, ex.end
c_repr = _bytes_repr if isinstance(ex, UnicodeDecodeError) else _text_repr
return ''''.join(c_repr(c) for c in s[start:end]), end
codecs.register_error(''backslashescape_backport'', backslashescape_backport)
print(b''/xc2/xa1/xa1after''.decode(''utf-8'', ''backslashescape_backport''))
print(u''/u2603''.encode(''latin1'', ''backslashescape_backport''))