2012-04-04 9 views
9

Sé que esto parece convirtiera fácilmente, y supongo que el problema es que simplemente no tienen una comprensión clara de todo esto bytes-str-Unicode (y codificación-decodificación, hablando francamente) cosas todavía.Decode cadena de base 64 en Python 3 (con lxml o no)

He intentado que mi código de trabajo se ejecute en Python 3. La parte a la que me ato es cuando analizo un XML con lxml y decodizo una cadena de base64 que está en ese XML.

El código ahora funciona de la siguiente manera:

puedo recuperar los datos binarios con una consulta XPath '.../binary/text()'. Esto produce una lista de un elemento que contiene un objeto lxml.etree._ElementUnicodeResult. Luego, con el pitón 2, que era capaz de hacer:

decoded = source.decode('base64') 

y finalmente

output = numpy.frombuffer(decoded) 

Sin embargo, el pitón 3 aparece un mensaje de error que dice

AttributeError: 'lxml.etree._ElementUnicodeResult' object has no attribute 'decode' 

Esto no es tan sorprendente, porque lxml.etree._ElementUnicodeResult es una subclase de str.

Otra forma sería la de obtener un verdadero str con los mismos datos en ella con

binary = tree.xpath('//binary')[0] 
binary_string = binary.text 

Eso sería esencialmente el mismo. Entonces, ¿qué debo hacer para descifrarlo desde base64? He mirado el módulo base64, pero se necesita un objeto bytes como argumento, y no puedo pensar en la forma de presentar str como , porque si trato de construir un objeto bytes, Python intentará codificar la cadena, que no necesito.

buscar en Google más, me encontré con el módulo binascii (que se invoca indirectamente de base64 todos modos, si no me equivoco), pero llamar binascii.b2a_base64() en mi cadena produce

TypeError: 'str' does not support the buffer interface 

P. S. Incluso encontré una pregunta respondida en how to decode a hex string in Python 3, pero esto se hace con un método dedicado bytes.fromhex(), así que no veo cómo sería útil.

¿Podría alguien decirme lo que me estoy perdiendo? Me temo que la mayor parte de la publicación es irrelevante y solo agrava mi vergüenza, pero al menos ustedes saben what I tried.

+4

Como nota aparte, Ned Batchelder tiene una gran presentación en este material de bytes-str-unicode: [Unicode pragmático, o: ¿Cómo detengo el dolor?] (Http://nedbatchelder.com/text/unipain.html) – delnan

+0

Gracias @delnan, estoy a la mitad y realmente ayuda mucho :) –

Respuesta

2

No tengo Python 3 instalado, pero parece que necesita convertir el Unicode devuelto de lxml a bytes, quizás llamando a .encode ('ascii')?

+0

Gosh ... Sabía que era fácil. No puedo resolver estas cosas en mi mente de la manera que debería ser. He estado pensando en mi cadena como algo * codificado *, así que realmente no se me ocurrió que necesito codificarlo para obtener 'bytes'. Gracias. –

+3

Piensa en Unicode como cadenas sencillas que deben codificarse cuando van a "hardware" y se decodifican cuando provienen de "hardware" :-) – thebjorn

+0

Me pareció que una pregunta tan larga necesitaba una respuesta más larga, pero de todos modos , muchas gracias por señalar la dirección correcta :) –

6

OK, creo que voy a resumir mi comprensión actual de las cosas (no dude en corregirme). Espero que ayude a alguien más por lo confuso que he estado.

El crédito va totalmente a thebjorn y delnan, por supuesto.

Por lo tanto, comenzando con las cosas más comunes: hay Unicode, y es un estándar global que asigna códigos (o puntos de código) a todos los caracteres exóticos que pueda imaginar. Esos códigos son solo números enteros. A partir de Unicode 6.1 hay 109,975 caracteres gráficos, dice Wikipedia.

Luego hay codificaciones que definen cómo designar caracteres Unicode con códigos de bytes. Un byte no es suficiente para designar un carácter Unicode arbitrario. Aunque, si solo toma un pequeño subconjunto de ellos (alfabeto inglés, dígitos, puntuación, algunos caracteres de control), puede hacerlo con un byte por carácter (o incluso 7 bits, consulte ASCII).


pasar una cadena Unicode en cualquier lugar, hay que codificarlo en bytes, entonces puede ser decodificada en el otro extremo.

En Python 2, str es en realidad bytes, y unicode es Unicode, pero Python 2 hará una codificación/descodificación implícita cuando sea necesario. Tratará de usar codificación ASCII.

En Python 3, str es siempre una cadena Unicode y bytes es un nuevo tipo de datos para bytes reales. Python 3 no realiza ninguna conversión implícita, siempre debe hacerlo usted mismo y especificar la codificación. Eso significa que su programa no funcionará hasta que comprenda lo que está pasando, lo cual me sucedió totalmente.


Ahora, que al ser más o menos claro, vamos a pasar a base64 codificación, que es también una codificación de tipo, pero tiene un significado ligeramente diferente. Supongamos que tiene algunos datos binarios (es decir, bytes) que pueden significar cualquier cosa (en mi caso se trata de un grupo de float s). Ahora quiere representar esta matriz binaria con una cadena. Eso es lo que significa la codificación base64: tiene sus bytes representados como una cadena ASCII.

Base64 significa 6 bits, por lo que en una cadena codificada en base64, un solo carácter representa 6 bits de sus datos. Es por eso que las cadenas codificadas en base64 deben tener una longitud que sea un múltiplo de 4: de lo contrario, el número de bytes codificados no será entero.


Finalmente, para decodificar desde base64 necesita una cadena ASCII. Una cadena Unicode no funcionará, solo puede haber caracteres del alfabeto base64. Base64 module hace el trabajo en Python. La función base64.b64decode() toma un cadena de bytes como argumento. En Python 2 significa: str. En Python 3 significa: bytes. Así que si usted tiene un str, como

>>> s = 'U3RhY2sgT3ZlcmZsb3c=' 

En Python 2 sólo podría hacer

>>> s.decode('base64') 

porque s ya está en ASCII. En Python 3, es necesario codificar en ASCII en primer lugar, por lo que tendrá que hacer:

>>> base64.b64decode(s.encode('ascii')) 

Y, por cierto, esto va a devolver un objeto bytes, por lo que es realmente depende de ti cómo para tratar esos bytes entonces.Tal vez sean mis carrozas, pero tal vez deberías intentar decodificarlo como ASCII :) En Python 2, sin embargo, será solo un str. De todos modos, eche un vistazo a struct para las herramientas para descomprimir sus datos de esos bytes.

Si necesita que el código funcione tanto en Python 2 como en 3, vaya con el último. Para asegurarse de que tiene Unicode en el final (si está decodificando texto de base 64), que tendrá que decodificarlo:

>>> base64.b64decode(s.encode('ascii')).decode('ascii') 

En Python 2, encode('ascii') no va a hacer nada de manera efectiva porque se aplica a str . Por lo tanto, primero realizará una conversión implícita a Unicode, y luego haga lo que desee (conviértalo nuevamente a ASCII). decode('ascii') devolverá un objeto unicode en Python 2.

+0

Resumen excelente :-) Si está tratando de guardar una lista de flotadores, ¿tal vez el módulo pickle será más fácil que el módulo struct? Algo como base64.b64encode (pickle.dumps ([2.718, 3.141])) – thebjorn

+0

@thebjorn Gracias :) En realidad estoy usando 'numpy.frombuffer()', acabo de mencionar 'struct' como referencia, para dar cuenta de un general caso. –

+0

"su programa no funcionará hasta que comprenda lo que está pasando": esto es algo bueno, la mayoría de las veces. :) – AKX