8

Tengo una cadena Unicode recuperada de un servicio web usando el requests module, que contiene los bytes de un documento binario (PCL, como ocurre). Uno de estos bytes tiene el valor 248, y el intento de base64 codifican conduce al error siguiente:base64 codificación cadenas unicode en python 2.7

In [68]: base64.b64encode(response_dict['content']+'\n') 
--------------------------------------------------------------------------- 
UnicodeEncodeError      Traceback (most recent call last) 
C:\...\<ipython-input-68-8c1f1913eb52> in <module>() 
----> 1 base64.b64encode(response_dict['content']+'\n') 

C:\Python27\Lib\base64.pyc in b64encode(s, altchars) 
    51  """ 
    52  # Strip off the trailing newline 
---> 53  encoded = binascii.b2a_base64(s)[:-1] 
    54  if altchars is not None: 
    55   return _translate(encoded, {'+': altchars[0], '/': altchars[1]}) 

UnicodeEncodeError: 'ascii' codec can't encode character u'\xf8' in position 272: ordinal not in range(128) 

In [69]: response_dict['content'].encode('base64') 
--------------------------------------------------------------------------- 
UnicodeEncodeError      Traceback (most recent call last) 
C:\...\<ipython-input-69-7fd349f35f04> in <module>() 
----> 1 response_dict['content'].encode('base64') 

C:\...\base64_codec.pyc in base64_encode(input, errors) 
    22  """ 
    23  assert errors == 'strict' 
---> 24  output = base64.encodestring(input) 
    25  return (output, len(input)) 
    26 

C:\Python27\Lib\base64.pyc in encodestring(s) 
    313  for i in range(0, len(s), MAXBINSIZE): 
    314   chunk = s[i : i + MAXBINSIZE] 
--> 315   pieces.append(binascii.b2a_base64(chunk)) 
    316  return "".join(pieces) 
    317 

UnicodeEncodeError: 'ascii' codec can't encode character u'\xf8' in position 44: ordinal not in range(128) 

Me parece un poco sorprendente, porque 248 está dentro de la gama de un byte sin signo (y puede mantenerse en una cadena de bytes), pero mi verdadera pregunta es: ¿cuál es la mejor o la forma correcta de codificar esta cadena?

Mi solución alternativa actual es la siguiente:

In [74]: byte_string = ''.join(map(compose(chr, ord), response_dict['content'])) 

In [75]: byte_string[272] 
Out[75]: '\xf8' 

Esto parece funcionar correctamente, y el byte_string resultante es capaz de ser codificada en base64, pero parece que no debe haber una mejor manera. ¿Esta ahí?

+1

248 puede estar dentro del rango de un byte sin signo, pero no está en el rango de ASCII estandarizado [0-127]. – Cameron

+0

@Cameron: un verdadero y buen punto, pero todavía no explica el problema, ya que el mismo valor exacto, cuando se mantiene en una cadena de bytes no da como resultado ese error. – Marcin

+0

Ver mi respuesta :-) Lo que has hecho es tomar los puntos de código de la cadena 'unicode' y tratarlos como bytes. Esto es ... sospechoso en el mejor de los casos, ya que no hay garantía de que los puntos de código estén dentro del rango 0-255. Lo que es aún peor es que nadie más sabrá cómo interpretar la cadena de bytes más adelante, ya que está en una codificación personalizada e indefinida. – Cameron

Respuesta

2

Dado que está trabajando con datos binarios, no estoy seguro de que sea una buena idea usar la codificación utf-8. Supongo que depende de cómo pretendas usar la representación codificada en base64. Creo que probablemente sería mejor si puede recuperar los datos como una cadena de bytes y no como una cadena Unicode. Nunca he usado la biblioteca de solicitudes, pero al examinar la documentación sugiero que es posible. Hay secciones que hablan sobre "Contenido de respuesta binaria" y "Contenido de respuesta sin formato".

+0

Gracias! Resulta que la codificación como latin-1 produce exactamente la misma secuencia de bytes que mi solución. – Marcin

+1

@Marcin: debe asegurarse de que el módulo de solicitudes no haya asumido que está trabajando con texto, haya aplicado una codificación predeterminada y decodificado sus datos binarios en Unicode. Si ese es el caso, tienes problemas. ¿Puedes verificar que el contenido es lo que esperas? –

+2

Después de prestar un poco más de atención a los documentos, resulta que las solicitudes también me dicen la codificación que se utiliza para decodificar la respuesta a unicode, por lo que siempre puedo volver a codificar con eso (y eso una vez más produce el mismo bytes). – Marcin

5

Yo sugeriría primera codificarlo en algo así como UTF-8 antes de la codificación base64:

In [12]: my_unicode = u'\xf8' 

In [13]: my_utf8 = my_unicode.encode('utf-8') 

In [15]: base64.b64encode(my_utf8) 
Out[15]: 'w7g=' 
+0

* codificación a UTF-8 * no tiene sentido. o codifica desde UTF-8 a bytes/ascii o decodifica desde ascii a UTF-8. es al revés. – sebix

14

usted tiene una cadena unicode que desea codificar en base64. El problema es que b64encode() solo funciona en bytes, no caracteres. Por lo tanto, debe transformar su cadena unicode (que es una secuencia de puntos de código Unicode abstractos) en una cadena de bytes.

El mapeo de cadenas Unicode abstractas en una serie concreta de bytes se llama que codifica. Python admite varias codificaciones; Sugiero que el ampliamente usado de codificación UTF-8:

byte_string = response_dict['content'].encode('utf-8') 

Nota que quien está decodificación de los bytes También necesitará saber qué codificación se utilizó para volver una cadena unicode a través de la complementaria decode() función:

# Decode 
decoded = byte_string.decode('utf-8') 

Un buen punto de partida para aprender más sobre Unicode y codificaciones es el Python docs, y el this article de Joel Spolsky.

+0

Para ser claros: los contenidos de mi cadena Unicode son datos binarios. No puedo cambiarlos a algunos bytes diferentes. ¿Hay una codificación de identidad? – Marcin

+1

@Marcin: no se puede tener una cadena 'unicode' que contenga datos binarios. ¡Esa es una contradicción en los términos! Si se supone que los bytes de la cadena 'unicode' representan datos binarios (como parece ser el caso aquí), entonces no deberían almacenarse en un objeto' unicode' ya que ¡en realidad no es Unicode en absoluto! – Cameron

+0

Sí, ese es mi problema. – Marcin

1

Debe ser posible obtener la respuesta como bytes binarios y omitir por completo los pasos de decodificación y codificación. Siempre existe la posibilidad de que requests elija una codificación que pierda algunos datos o errores en el viaje de ida y vuelta.

Esta parte de los documentos llamados "Binary Response Content" parece ajustarse perfectamente a su problema.

0

Si se trata de datos binarios ... ¿por qué codificar/decodificar en absoluto? Especialmente la parte "base64.encodestring".A continuación se muestra cómo codifico imágenes en base64 para agregarlas directamente a mi código python en lugar de tener archivos adicionales. 2.7.2 btw

import base64 
iconfile = open("blah.icon","rb") 
icondata = iconfile.read() 
icondata = base64.b64encode(icondata)