Cadena a Bytes Python sin cambio en la encoding.

Tengo este problema y no sé cómo resolverlo. Tengo esta cadena:

data = '\xc4\xb7\x86\x17\xcd' 

Cuando intenté codificarlo:

 data.encode() 

Obtengo este resultado:

 b'\xc3\x84\xc2\xb7\xc2\x86\x17\xc3\x8d' 

Yo solo quiero:

 b'\xc4\xb7\x86\x17\xcd' 

Alguien sabe la razón y cómo solucionar esto. La cadena ya está almacenada en una variable, por lo que no puedo agregar el literal b delante de ella.

No puede convertir una cadena en bytes o bytes en una cadena sin tener en cuenta una encoding. El punto entero sobre el tipo de bytes es una secuencia de bytes independiente de la encoding, mientras que str es una secuencia de puntos de código Unicode que, por diseño , no tienen una representación de bytes única.

Por lo tanto, cuando desee convertir uno en otro, debe indicar explícitamente qué encoding desea utilizar para realizar esta conversión. Al convertir en bytes, debe decir cómo representar cada carácter como una secuencia de bytes; y cuando realiza la conversión de bytes, debe indicar qué método utilizar para asignar esos bytes a caracteres.

Si no especifica la encoding, entonces UTF-8 es el valor predeterminado, que es un valor predeterminado razonable ya que UTF-8 es ubicuo, pero también es solo una de las muchas codificaciones válidas.

Si tomas tu cadena original, '\xc4\xb7\x86\x17\xcd' , observa qué puntos de código de Unicode representan estos caracteres. \xc4 por ejemplo es la LATIN CAPITAL LETTER A WITH DIAERESIS , es decir, Ä . Ese carácter está codificado en UTF-8 como 0xC3 0x84 que explica por qué eso es lo que obtienes cuando lo codificas en bytes. Pero también tiene una encoding de 0x00C4 en UTF-16, por ejemplo.


En cuanto a cómo resolver esto correctamente para que obtenga el resultado deseado, no hay una respuesta clara clara. La solución que Kasramvd mencionó también es algo imperfecta. Si has leído sobre el codec raw_unicode_escape en la documentación :

raw_unicode_escape

Codificación Latin-1 con \uXXXX y \UXXXXXXXX para otros puntos de código. Las barras invertidas existentes no se escapan de ninguna manera. Se utiliza en el protocolo de pickle de Python.

Así que esto es solo una encoding Latin-1 que tiene un respaldo incorporado para los caracteres que están fuera de él. Yo consideraría esta alternativa algo dañina para su propósito. Para los caracteres Unicode que no se pueden representar como una secuencia \xXX , esto podría ser problemático:

 >>> chr(256).encode('raw_unicode_escape') b'\\u0100' 

Entonces, el punto de código 256 está explícitamente fuera de Latin-1, lo que hace que la encoding raw_unicode_escape devuelva los bytes codificados para la cadena '\\u0100' , convirtiendo ese carácter en 6 bytes que tienen poco que ver con el carácter original (ya que es una secuencia de escape).

Entonces, si quisieras usar Latin-1 aquí, te sugeriría que lo utilices explícitamente, sin tener la alternativa de secuencia de escape de raw_unicode_escape . Esto simplemente provocará una excepción al intentar convertir puntos de código fuera del área de Latin-1:

 >>> '\xc4\xb7\x86\x17\xcd'.encode('latin1') b'\xc4\xb7\x86\x17\xcd' >>> chr(256).encode('latin1') Traceback (most recent call last): File "", line 1, in  chr(256).encode('latin1') UnicodeEncodeError: 'latin-1' codec can't encode character '\u0100' in position 0: ordinal not in range(256) 

Por supuesto, si los puntos de código fuera del área de Latin-1 pueden o no causarle problemas, depende de dónde provenga esa cadena. Pero si puede garantizar que la entrada solo contendrá caracteres válidos de Latin-1, es probable que no necesite trabajar con una cadena en primer lugar. Dado que en realidad está tratando con algún tipo de bytes, debería mirar si no puede simplemente recuperar esos valores como bytes en primer lugar. De esa manera, no introducirás dos niveles de encoding allí donde puedas corromper los datos al interpretar mal la entrada.

Puedes usar 'raw_unicode_escape' como tu encoding:

 In [14]: bytes(data, 'raw_unicode_escape') Out[14]: b'\xc4\xb7\x86\x17\xcd' 

Como se mencionó en los comentarios, también puede pasar la encoding directamente al método de encode de su cadena.

 In [15]: data.encode("raw_unicode_escape") Out[15]: b'\xc4\xb7\x86\x17\xcd'