Reemplazar eficientemente los caracteres malos

A menudo trabajo con texto utf-8 que contiene caracteres como:

\ xc2 \ x99

\ xc2 \ x95

\ xc2 \ x85

etc

Estos caracteres confunden otras bibliotecas con las que trabajo, por lo que deben ser reemplazados.

¿Qué es una forma eficiente de hacer esto, en lugar de:

text.replace('\xc2\x99', ' ').replace('\xc2\x85, '...') 

Siempre hay expresiones regulares; simplemente enumera todos los caracteres ofensivos dentro de corchetes, así:

 import re print re.sub(r'[\xc2\x99]'," ","Hello\xc2There\x99") 

Esto imprime: ‘Hola’, con los caracteres no deseados reemplazados por espacios.

Alternativamente, si tienes un personaje de reemplazo diferente para cada uno:

 # remove annoying characters chars = { '\xc2\x82' : ',', # High code comma '\xc2\x84' : ',,', # High code double comma '\xc2\x85' : '...', # Tripple dot '\xc2\x88' : '^', # High carat '\xc2\x91' : '\x27', # Forward single quote '\xc2\x92' : '\x27', # Reverse single quote '\xc2\x93' : '\x22', # Forward double quote '\xc2\x94' : '\x22', # Reverse double quote '\xc2\x95' : ' ', '\xc2\x96' : '-', # High hyphen '\xc2\x97' : '--', # Double hyphen '\xc2\x99' : ' ', '\xc2\xa0' : ' ', '\xc2\xa6' : '|', # Split vertical bar '\xc2\xab' : '<<', # Double less than '\xc2\xbb' : '>>', # Double greater than '\xc2\xbc' : '1/4', # one quarter '\xc2\xbd' : '1/2', # one half '\xc2\xbe' : '3/4', # three quarters '\xca\xbf' : '\x27', # c-single quote '\xcc\xa8' : '', # modifier - under curve '\xcc\xb1' : '' # modifier - under line } def replace_chars(match): char = match.group(0) return chars[char] return re.sub('(' + '|'.join(chars.keys()) + ')', replace_chars, text) 

Creo que hay un problema subyacente aquí, y podría ser una buena idea investigar y resolverlo, en lugar de tratar de encubrir los síntomas.

\xc2\x95 es la encoding UTF-8 del carácter U + 0095, que es un carácter de control C1 (MESSAGE WAITING). No es de extrañar que su biblioteca no pueda manejarlo. Pero la pregunta es, ¿cómo llegó a sus datos?

Bueno, una posibilidad muy probable es que comenzó como el carácter 0x95 (BULLET) en la encoding de Windows-1252 , se decodificó erróneamente como U + 0095 en lugar del U + 2022 correcto, y luego se codificó en UTF-8. (El término japonés mojibake describe este tipo de error).

Si esto es correcto, entonces puede recuperar los caracteres originales volviéndolos a colocar en Windows-1252 y luego decodificarlos en Unicode correctamente esta vez. (En estos ejemplos estoy usando Python 3.3; estas operaciones son un poco diferentes en Python 2.)

 >>> b'\x95'.decode('windows-1252') '\u2022' >>> import unicodedata >>> unicodedata.name(_) 'BULLET' 

Si desea hacer esta corrección para todos los caracteres en el rango 0x80–0x99 que son caracteres válidos de Windows-1252, puede utilizar este método:

 def restre_windows_1252_characters(s): """Replace C1 control characters in the Unicode string s by the characters at the corresponding code points in Windows-1252, where possible. """ import re def to_windows_1252(match): try: return bytes([ord(match.group(0))]).decode('windows-1252') except UnicodeDecodeError: # No character at the corresponding code point: remove it. return '' return re.sub(r'[\u0080-\u0099]', to_windows_1252, s) 

Por ejemplo:

 >>> restre_windows_1252_characters('\x95\x99\x85') '•™…' 

Si desea eliminar todos los caracteres que no son ASCII de una cadena, puede usar

 text.encode("ascii", "ignore") 
 import unicodedata # Convert to unicode text_to_uncicode = unicode(text, "utf-8") # Convert back to ascii text_fixed = unicodedata.normalize('NFKD',text_to_unicode).encode('ascii','ignore') 

Esto no es “caracteres Unicode”; se parece más a esto a una cadena codificada en UTF-8. (Aunque su prefijo debe ser \ xC3, no \ xC2 para la mayoría de los caracteres). No debe tirarlos en el 95% de los casos, a menos que se esté comunicando con un back-end de COBOL. El mundo no se limita a 26 caracteres, ya sabes.

Hay una lectura concisa para explicar las diferencias entre las cadenas Unicode (lo que se usa como un objeto Unicode en Python 2 y como las cadenas en Python 3 aquí: http://www.joelonsoftware.com/articles/Unicode.html – por favor, para Lea todo esto. Incluso si nunca planea tener algo que no sea el inglés en todas sus aplicaciones, seguirá tropezando con símbolos como € o º que no caben en ASCII de 7 bits. Ese artículo le ayudará .

Dicho esto, tal vez las bibliotecas que estás utilizando acepten objetos Python Unicode, y puedes transformar tus cadenas UTF-8 Python 2 en unidoce haciendo lo siguiente:

 var_unicode = var.decode("utf-8") 

Si realmente necesitas un ASCII 100% puro, reemplaza todos los caracteres que no son ASCII, luego de decodificar la cadena a Unicode, recodifíquelo a ASCII, diciéndole que ignore los caracteres que no encajan en el conjunto de caracteres con:

 var_ascii = var_unicode.encode("ascii", "replace") 

Estos caracteres no están en la Biblioteca ASCII y esa es la razón por la que está recibiendo los errores. Para evitar estos errores, puede hacer lo siguiente mientras lee el archivo.

 import codecs f = codecs.open('file.txt', 'r',encoding='utf-8') 

Para saber más sobre este tipo de errores, vaya a través de este enlace .