Problema de deencoding de la cadena Python

Estoy intentando analizar un archivo CSV que contiene algunos datos, en su mayoría numéricos pero con algunas cadenas, que no conozco su encoding, pero sí sé que están en hebreo.

Eventualmente, necesito saber la encoding para poder unicodificar las cadenas, imprimirlas y tal vez colocarlas en una base de datos más adelante.

Intenté usar Chardet , que afirma que las cadenas son Windows-1255 ( cp1255 ) pero al intentar print someString.decode('cp1255') produce el error notorio:

 UnicodeEncodeError: 'ascii' codec can't encode characters in position 1-4: ordinal not in range(128) 

Intenté cualquier otra encoding posible, en vano. Además, el archivo es absolutamente válido ya que puedo abrir el CSV en Excel y veo los datos correctos.

¿Alguna idea de cómo puedo decodificar adecuadamente estas cadenas?


EDITAR: aquí hay un ejemplo. Una de las cuerdas se parece a esto (primeras cinco letras del alfabeto hebreo):

 print repr(sampleString) #prints: '\xe0\xe1\xe2\xe3\xe4' 

(utilizando Python 2.6.2)

Esto es lo que está pasando:

  • sampleString es una cadena de bytes (codificada cp1255)
  • sampleString.decode("cp1255") decodifica (decodifica == bytes -> cadena Unicode) la cadena de bytes a una cadena Unicode
  • print sampleString.decode("cp1255") intenta imprimir la cadena Unicode en stdout. La impresión debe codificar la cadena de Unicode para hacer eso (codificar == cadena de Unicode -> bytes). El error que está viendo significa que la statement de impresión de Python no puede escribir la cadena Unicode dada en la encoding de la consola. sys.stdout.encoding es la encoding del terminal.

Entonces el problema es que tu consola no soporta estos caracteres. Debería poder ajustar la consola para usar otra encoding. Los detalles sobre cómo hacerlo dependen de su sistema operativo y su progtwig de terminal.

Otro enfoque sería especificar manualmente la encoding a usar:

 print sampleString.decode("cp1255").encode("utf-8") 

Ver también:

Un progtwig de prueba simple con el que puedes experimentar:

 import sys print sys.stdout.encoding samplestring = '\xe0\xe1\xe2\xe3\xe4' print samplestring.decode("cp1255").encode(sys.argv[1]) 

En mi terminal utf-8:

 $ python2.6 test.py utf-8 UTF-8 אבגדה $ python2.6 test.py latin1 UTF-8 Traceback (most recent call last): UnicodeEncodeError: 'latin-1' codec can't encode characters in position 0-4: ordinal not in range(256) $ python2.6 test.py ascii UTF-8 Traceback (most recent call last): UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128) $ python2.6 test.py cp424 UTF-8 ABCDE $ python2.6 test.py iso8859_8 UTF-8       

Los mensajes de error para latin-1 y ascii significan que los caracteres Unicode en la cadena no se pueden representar en estas codificaciones.

Note los dos últimos. Codifico la cadena Unicode a las codificaciones cp424 e iso8859_8 (dos de las codificaciones enumeradas en http://docs.python.org/library/codecs.html#standard-encodings que admiten caracteres hebreos). No obtengo ninguna excepción al usar estas codificaciones, ya que los caracteres hebreos de unicode tienen una representación en las codificaciones.

Pero mi terminal utf-8 se confunde mucho cuando recibe bytes en una encoding diferente a la de utf-8.

En el primer caso (cp424), mi terminal UTF-8 muestra ABCDE, lo que significa que la representación utf-8 de A corresponde a la representación cp424 de ה, es decir, el valor de byte 65 significa A en utf-8 y ה en cp424.

El método de encode tiene un argumento de cadena opcional que puede usar para especificar qué debe suceder cuando la encoding no puede representar un carácter ( documentación ). Las estrategias admitidas son estrictas (las predeterminadas), ignorar, reemplazar, xmlcharref y backslashreplace. Incluso puedes agregar tus propias estrategias personalizadas .

Otro progtwig de prueba (imprimo con comillas alrededor de la cadena para mostrar mejor cómo se comporta ignorar):

 import sys samplestring = '\xe0\xe1\xe2\xe3\xe4' print "'{0}'".format(samplestring.decode("cp1255").encode(sys.argv[1], sys.argv[2])) 

Los resultados:

 $ python2.6 test.py latin1 strict Traceback (most recent call last): File "test.py", line 4, in  sys.argv[2])) UnicodeEncodeError: 'latin-1' codec can't encode characters in position 0-4: ordinal not in range(256) [/tmp] $ python2.6 test.py latin1 ignore '' [/tmp] $ python2.6 test.py latin1 replace '?????' [/tmp] $ python2.6 test.py latin1 xmlcharrefreplace 'אבגדה' [/tmp] $ python2.6 test.py latin1 backslashreplace '\u05d0\u05d1\u05d2\u05d3\u05d4' 

Cuando decodifica la cadena a Unicode con someString.decode('cp1255') , tiene una representación abstracta de algún texto hebreo en Unicode. (¡Esta parte sucede con éxito!) Cuando utiliza la print , necesita una representación concreta y codificada en una encoding específica. Parece que su problema no es con la deencoding, sino con la print .

Para imprimir, simplemente print someString si su terminal entiende cp1255 o ” print someString.decode('cp1255').encode('the_encoding_your_terminal_does_understand') “. Si no necesita que la impresión resultante se pueda leer en hebreo, print repr(someString.decode('cp1255')) también le print repr(someString.decode('cp1255')) una representación significativa de la cadena abstracta de Unicode.

Recibirá un error de encoding al imprimir, así que lo más probable es que la deencoding esté bien, simplemente no puede imprimir el resultado correctamente. Intente ejecutar chcp 65001 en el símbolo del sistema antes de iniciar el código Python.

¿Es posible que someString no sea una cadena normal, sino una cadena Unicode, como nos haría creer con su sampleString ?

 >>> print '\xe0\xe1\xe2\xe3\xe4'.decode('cp1255')  >>> print u'\xe0\xe1\xe2\xe3\xe4'.decode('cp1255') Traceback (most recent call last): File "", line 1, in  File "[...]/encodings/cp1255.py", line 15, in decode return codecs.charmap_decode(input,errors,decoding_table) UnicodeEncodeError: 'ascii' codec can't encode characters [...]