2010-04-09 16 views
10

Tengo un programa de Python que almacena y escribe datos en un archivo. Los datos son datos binarios en bruto, almacenados internamente como str. Lo escribo a través de un códec utf-8. Sin embargo, obtengo UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 25: character maps to <undefined> en el archivo cp1252.py.¿Cómo escribo datos binarios sin procesar en Python?

Me parece que Python está tratando de interpretar los datos usando la página de códigos predeterminada. Pero no es tiene una página de códigos predeterminada. Es por eso que estoy usando str, no unicode.

Creo que mis preguntas son:

  • ¿Cómo se representan los datos binarios sin procesar en la memoria, en Python?
  • Cuando estoy escribiendo datos binarios en bruto a través de un códec, ¿cómo puedo codificar/descodificar?
+1

Dice que tiene datos sin formato de una vez, ¿cómo está generando esos datos en primer lugar? Supongo que tienes una fuente Unicode en alguna parte, pero no tengo claro si estás escribiendo unicode "en bruto" en str, o si lo estás leyendo desde un archivo (como objeto) o ... (¡Publicar una cadena de ejemplo que demuestre este error sería útil!) –

Respuesta

21

NOTA: esto fue escrito para Python 2.x No estoy seguro si es aplicable a 3.x.

Su uso de str para datos binarios sin procesar en la memoria es correcto.
[Si está usando Python 2.6+, es incluso mejor usar bytes que en 2.6+ es solo un alias de str pero expresa su intención mejor, y le ayudará si un día transfiere el código a Python 3.]

Como otros señalan, escribir datos binarios a través de un códec es extraño. Un códec de escritura toma unicode y emite bytes en el archivo. Intentas hacerlo al revés, de ahí nuestra confusión sobre tus intenciones ...

[Y tu diagnóstico del error parece correcto: dado que el códec espera unicode, Python está decodificando tu str en unicode con la codificación predeterminada del sistema , que se ahoga.]

¿Qué desea ver en el archivo de salida?

  • Si el archivo debe contener los datos binarios como está:

    Entonces no hay que enviarlo a través de un códec; debe escribirlo directamente en el archivo.Un códec codifica todo y solo puede emitir codificaciones válidas de Unicode (en su caso, UTF-8 válido). No hay entrada que pueda darle para emitir secuencias de bytes arbitrarias !

    • Si necesita una mezcla de datos binarios UTF-8 y primas, que debe abrir el archivo directamente, y se entremezclan escribe de some_data con some_text.encode('utf8') ...

    Nota sin embargo, que mezclar UTF-8 con datos arbitrarios crudos es muy mal diseño, porque tales archivos son muy inconvenientes para tratar con! Las herramientas que entienden Unicode se ahogarán en los datos binarios de , dejándote con una manera no conveniente de ver (ni hablar de modificar) el archivo.

  • Si desea usar una representación de bytes arbitrarias en Unicode:

    Pass data.encode('base64') al codec. Base64 produce solo ascii limpio (letras, números y un pequeño signo de puntuación) para que se pueda incrustar claramente en cualquier elemento, claramente se ve a las personas como datos binarios, y es razonablemente compacto (ligeramente más del 33% de sobrecarga).

    P.S. Puede notar que data.encode('base64') es extraño.

    • .encode() se supone que tomar Unicode pero yo estoy dando una cadena ?! Python tiene varios pseudo-codecs que convierten str-> str como 'base64' y 'zlib'.

    • .encode() siempre devuelve un str pero lo alimentarás en un códec esperando unicode ?! En este caso, solo contendrá limpio ascii, por lo que no importa. Puede escribir explícitamente data.encode('base64').encode('utf8') si le hace sentir mejor.

  • Si necesita un mapeado 1: 1 de bytes arbitrarias a unicode:

    Pass data.decode('latin1') al codec. latin1 mapas bytes 0-255 para caracteres unicode 0-255, que es un poco elegante.

    El códec será, por supuesto, codificar sus caracteres - 128-255 son codificado como 2 o 3 bytes en UTF-8 (sorprendentemente, el promedio sobrecarga es 50%, más de base64!). Esto mata bastante la "elegancia" de tener un mapeo 1: 1.

    Tenga en cuenta también que los caracteres Unicode 0-255 incluyen personajes desagradables invisible/control (salto de línea, salto de página, guión de separación, etc.) haciendo que sus datos binarios molesto para ver en editores de texto.

    Teniendo en cuenta estos inconvenientes, No recomiendo latin1 a menos que entienda exactamente por qué lo quiere.
    Solo lo menciono como la otra codificación "natural" que surge en .

0

Normalmente no debería utilizar códecs con str, excepto para convertirlos en unicode s. Tal vez debería considerar el uso del códec latin-1 si cree que desea datos "sin procesar" en sus Unicodes.

+0

No quiero datos "en bruto" en mis unicodes. –

+0

Entonces, ¿por qué estás usando un códec? –

+0

Estoy escribiendo datos binarios en bruto en un archivo de texto, junto con varias cadenas de caracteres unicode. Cuando intento escribir los datos binarios en bruto (que he almacenado internamente en formato utf-8) en un códec utf-8, aparece el error cp1252. –

0

Para su primera pregunta: en Python, , las cadenas normales (es decir, cadenas no unicode) son datos binarios. Si desea escribir las cadenas Unicode y datos binarios, convertir las cadenas Unicode en datos binarios y ponerlos juntos:

# encode the unicode string as a string 
bytes = unicodeString.encode('utf-8') 
# add it to the other string 
raw_data += bytes 
# write it all to a file 
yourFile.write(raw_data) 

Para su segunda pregunta: write() que los datos en bruto; entonces, cuando lo lee, lo hace de esta manera:

import codecs 
yourFile = codecs.open("yourFileName", "r", "utf-8") 
# and now just use yourFile.read() to read it 
+0

Como mencioné, tengo * una cadena normal. –

+0

Y hacer 'yourFile.write (regular_string)' le da el error? No es necesario codificar más una cadena regular; como dije, ya son bytes sin formato. –

+0

@Chris: ¿Estás haciendo algo tan tonto como usar Python 3, quizás? – SamB

Cuestiones relacionadas