xticks xlabel python unicode encoding utf-8 windows-1252

xlabel - Python-tratar con archivos de codificación mixta



plotly title (2)

Gracias a jsbueno y a otras búsquedas de Google y otros golpes, lo resolví de esta manera.

#The following works very well but it does not allow for any attempts to FIX the data. xmlText = unicode(xmlText, errors=''replace'').replace(u"/uFFFD", "?")

Esta versión permite una oportunidad limitada para reparar caracteres no válidos. Los caracteres desconocidos se reemplazan con un valor seguro.

import codecs replacement = { ''85'' : ''...'', # u''/u2026'' ... character. ''96'' : ''-'', # u''/u2013'' en-dash ''97'' : ''-'', # u''/u2014'' em-dash ''91'' : "''", # u''/u2018'' left single quote ''92'' : "''", # u''/u2019'' right single quote ''93'' : ''"'', # u''/u201C'' left double quote ''94'' : ''"'', # u''/u201D'' right double quote ''95'' : "*" # u''/u2022'' bullet } #This is is more complex but allows for the data to be fixed. def mixed_decoder(unicodeError): errStr = unicodeError[1] errLen = unicodeError.end - unicodeError.start nextPosition = unicodeError.start + errLen errHex = errStr[unicodeError.start:unicodeError.end].encode(''hex'') if errHex in replacement: return u''%s'' % replacement[errHex], nextPosition return u''%s'' % errHex, nextPosition # Comment this line out to get a question mark return u''?'', nextPosition codecs.register_error("mixed", mixed_decoder) xmlText = xmlText.decode("utf-8", "mixed")

Básicamente intento convertirlo en utf8. Para cualquier personaje que falle, simplemente lo convierto a HEX para que pueda mostrarlo o buscarlo en mi propia tabla.

Esto no es bonito pero me permite dar sentido a los datos desordenados

Tengo un archivo que es en su mayoría UTF-8, pero algunos caracteres de Windows-1252 también se han introducido.

Creé una tabla para asignar desde los caracteres de Windows-1252 (cp1252) a sus contrapartes de Unicode, y me gustaría usarla para corregir los caracteres mal codificados, por ejemplo

cp1252_to_unicode = { "/x85": u''/u2026'', # … "/x91": u''/u2018'', # ‘ "/x92": u''/u2019'', # ’ "/x93": u''/u201c'', # “ "/x94": u''/u201d'', # ” "/x97": u''/u2014'' # — } for l in open(''file.txt''): for c, u in cp1252_to_unicode.items(): l = l.replace(c, u)

Pero al intentar realizar la sustitución de esta manera, se genera un error UnicodeDecodeError, por ejemplo:

"/x85".replace("/x85", u''/u2026'') UnicodeDecodeError: ''ascii'' codec can''t decode byte 0x85 in position 0: ordinal not in range(128)

¿Alguna idea de cómo lidiar con esto?


Si intenta decodificar esta cadena como utf-8, como ya sabe, obtendrá un error "UnicodeDecode", ya que estos caracteres espurios cp1252 no son válidos utf-8 -

Sin embargo, los codecs de Python le permiten registrar una devolución de llamada para manejar los errores de codificación / decodificación , con la función codecs.register_error - obtiene el parámetro UnicodeDecodeerror aa - puede escribir un controlador que intente descifrar los datos como "cp1252", y continúa la decodificación en utf-8 para el resto de la cadena.

En mi terminal utf-8, puedo construir una cadena incorrecta mixta como esta:

>>> a = u"maçã ".encode("utf-8") + u"maçã ".encode("cp1252") >>> print a maçã ma�� >>> a.decode("utf-8") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.6/encodings/utf_8.py", line 16, in decode return codecs.utf_8_decode(input, errors, True) UnicodeDecodeError: ''utf8'' codec can''t decode bytes in position 9-11: invalid data

Escribí la mencionada función de devolución de llamada aquí y encontré una captura: incluso si incrementas la posición desde la que decodificas la cadena en 1, para que comience en el siguiente chratcer, si el siguiente carácter tampoco es utf-8 y sale del rango (128), el error se genera en el primer carácter fuera del rango (128), lo que significa que la decodificación "retrocede" si se encuentran caracteres consecutivos no ascii, no utf-8.

El objetivo de esto es tener una variable de estado en el error_handler que detecte este "retroceso" y reanudar la descodificación desde la última llamada a ella. En este breve ejemplo, lo implementé como una variable global (tendrá que ser manual). reajuste a "-1" antes de cada llamada al decodificador):

import codecs last_position = -1 def mixed_decoder(unicode_error): global last_position string = unicode_error[1] position = unicode_error.start if position <= last_position: position = last_position + 1 last_position = position new_char = string[position].decode("cp1252") #new_char = u"_" return new_char, position + 1 codecs.register_error("mixed", mixed_decoder)

Y en la consola:

>>> a = u"maçã ".encode("utf-8") + u"maçã ".encode("cp1252") >>> last_position = -1 >>> print a.decode("utf-8", "mixed") maçã maçã