Eliminar caracteres de control de una cadena en Python

Actualmente tengo el siguiente código

def removeControlCharacters(line): i = 0 for c in line: if (c < chr(32)): line = line[:i - 1] + line[i+1:] i += 1 return line 

Esto simplemente no funciona si hay más de un carácter para ser eliminado.

Hay cientos de caracteres de control en Unicode. Si está limpiando datos de la web o de alguna otra fuente que pueda contener caracteres que no sean ASCII, necesitará el módulo Unicodedata de Python. La función unicodedata.category(…) devuelve el código de categoría Unicode (por ejemplo, caracteres de control, espacios en blanco, letras, etc.) de cualquier carácter. Para los caracteres de control, la categoría siempre comienza con “C”.

Este fragmento de código elimina todos los caracteres de control de una cadena.

 import unicodedata def remove_control_characters(s): return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C") 

Ejemplos de categorías Unicode :

 >>> from unicodedata import category >>> category('\r') # carriage return --> Cc : control character 'Cc' >>> category('\0') # null character ---> Cc : control character 'Cc' >>> category('\t') # tab --------------> Cc : control character 'Cc' >>> category(' ') # space ------------> Zs : separator, space 'Zs' >>> category(u'\u200A') # hair space -------> Zs : separator, space 'Zs' >>> category(u'\u200b') # zero width space -> Cf : control character, formatting 'Cf' >>> category('A') # letter "A" -------> Lu : letter, uppercase 'Lu' >>> category(u'\u4e21') # 両 ---------------> Lo : letter, other 'Lo' >>> category(',') # comma -----------> Po : punctuation 'Po' >>> 

Podría usar str.translate con el mapa apropiado, por ejemplo, como este:

 >>> mpa = dict.fromkeys(range(32)) >>> 'abc\02de'.translate(mpa) 'abcde' 

Cualquier persona interesada en una clase de caracteres de [\x00-\x1f\x7f-\x9f] que coincida con cualquier carácter de control de Unicode puede usar [\x00-\x1f\x7f-\x9f] .

Puedes probarlo así:

 >>> import unicodedata, re, sys >>> all_chars = [chr(i) for i in range(sys.maxunicode)] >>> control_chars = ''.join(c for c in all_chars if unicodedata.category(c) == 'Cc') >>> expanded_class = ''.join(c for c in all_chars if re.match(r'[\x00-\x1f\x7f-\x9f]', c)) >>> control_chars == expanded_class True 

Así que para eliminar los caracteres de control con re solo use lo siguiente:

 >>> re.sub(r'[\x00-\x1f\x7f-\x9f]', '', 'abc\02de') 'abcde' 

Su implementación es incorrecta porque el valor de i es incorrecto. Sin embargo, ese no es el único problema: también utiliza repetidamente las operaciones de cadena lenta, lo que significa que se ejecuta en O (n 2 ) en lugar de O (n). Intenta esto en su lugar:

 return ''.join(c for c in line if ord(c) >= 32) 

Y para Python 2, con la translate incorporada:

 import string all_bytes = string.maketrans('', '') # String of 256 characters with (byte) value 0 to 255 line.translate(all_bytes, all_bytes[:32]) # All bytes < 32 are deleted (the second argument lists the bytes to delete) 

Modificas la línea durante la iteración sobre ella. Algo como ''.join([x for x in line if ord(x) >= 32])

 filter(string.printable[:-5].__contains__,line) 

Esta es la forma más fácil, más completa y más robusta que conozco. Sin embargo, sí requiere una dependencia externa. Considero que vale la pena para la mayoría de los proyectos.

 pip install regex import regex as re def remove_control_characters(str): return re.sub(r'\p{C}', '', 'my-string') 

\p{C} es la propiedad de caracteres Unicode para los caracteres de control, por lo que puede dejarla en manos del consorcio Unicode, y los millones de caracteres disponibles en Unicode deben considerarse control. También hay otras propiedades de caracteres extremadamente útiles que uso con frecuencia, por ejemplo \p{Z} para cualquier tipo de espacio en blanco.