Desafortunadamente, la iteración sobre cadenas es bastante lenta en Python. Las expresiones regulares son más rápidas en un orden de magnitud para este tipo de cosas. Solo tienes que construir la clase de personaje tú mismo. El módulo unicodedata es bastante útil para esto, especialmente la función unicodedata.category () . Consulte Base de datos de caracteres Unicode para obtener descripciones de las categorías.
import unicodedata, re, itertools, sys
all_chars = (chr(i) for i in range(sys.maxunicode))
categories = {'Cc'}
control_chars = ''.join(c for c in all_chars if unicodedata.category(c) in categories)
control_chars = ''.join(map(chr, itertools.chain(range(0x00,0x20), range(0x7f,0xa0))))
control_char_re = re.compile('[%s]' % re.escape(control_chars))
def remove_control_chars(s):
return control_char_re.sub('', s)
Para Python2
import unicodedata, re, sys
all_chars = (unichr(i) for i in xrange(sys.maxunicode))
categories = {'Cc'}
control_chars = ''.join(c for c in all_chars if unicodedata.category(c) in categories)
control_chars = ''.join(map(unichr, range(0x00,0x20) + range(0x7f,0xa0)))
control_char_re = re.compile('[%s]' % re.escape(control_chars))
def remove_control_chars(s):
return control_char_re.sub('', s)
Para algunos casos de uso, pueden ser preferibles categorías adicionales (por ejemplo, todos los del grupo de control , aunque esto podría ralentizar el tiempo de procesamiento y aumentar el uso de la memoria de manera significativa. Número de caracteres por categoría:
Cc
(control): 65
Cf
(formato): 161
Cs
(sustituto): 2048
Co
(uso privado): 137468
Cn
(no asignado): 836601
Editar Agregar sugerencias de los comentarios.
all_chars = (unichr(i) for i in xrange(sys.maxunicode))
para evitar el error de construcción estrecho.control_chars == '\x00-\x1f\x7f-\x9f'
(probado en Python 3.5.2)Hasta donde yo sé, el método más pitónico / eficiente sería:
import string filtered_string = filter(lambda x: x in string.printable, myStr)
fuente
Puede intentar configurar un filtro usando la
unicodedata.category()
función:import unicodedata printable = {'Lu', 'Ll'} def filter_non_printable(str): return ''.join(c for c in str if unicodedata.category(c) in printable)
Consulte la Tabla 4-9 en la página 175 en las propiedades de caracteres de la base de datos Unicode para conocer las categorías disponibles.
fuente
printable = {'Lu', 'Ll', Zs', 'Nd'}
En Python 3,
def filter_nonprintable(text): import itertools # Use characters of control category nonprintable = itertools.chain(range(0x00,0x20),range(0x7f,0xa0)) # Use translate to remove all non-printable characters return text.translate({character:None for character in nonprintable})
Vea esta publicación de StackOverflow sobre cómo eliminar la puntuación para ver cómo se compara .translate () con regex & .replace ()
Los rangos se pueden generar mediante el
nonprintable = (ord(c) for c in (chr(i) for i in range(sys.maxunicode)) if unicodedata.category(c)=='Cc')
uso de las categorías de la base de datos de caracteres Unicode como lo muestra @Ants Aasma.fuente
text.translate({c:None for c in itertools.chain(range(0x00,0x20),range(0x7f,0xa0))})
.Lo siguiente funcionará con la entrada Unicode y es bastante rápido ...
import sys # build a table mapping all non-printable characters to None NOPRINT_TRANS_TABLE = { i: None for i in range(0, sys.maxunicode + 1) if not chr(i).isprintable() } def make_printable(s): """Replace non-printable characters in a string.""" # the translate method on str removes characters # that map to None from the string return s.translate(NOPRINT_TRANS_TABLE) assert make_printable('Café') == 'Café' assert make_printable('\x00\x11Hello') == 'Hello' assert make_printable('') == ''
Mis propias pruebas sugieren que este enfoque es más rápido que las funciones que iteran sobre la cadena y devuelven un resultado usando
str.join
.fuente
LINE_BREAK_CHARACTERS = set(["\n", "\r"])
yand not chr(i) in LINE_BREAK_CHARACTERS
al crear la tabla.Esta función usa listas por comprensión y str.join, por lo que se ejecuta en tiempo lineal en lugar de O (n ^ 2):
from curses.ascii import isprint def printable(input): return ''.join(char for char in input if isprint(char))
fuente
filter(isprint,input)
Otra opción más en Python 3:
re.sub(f'[^{re.escape(string.printable)}]', '', my_string)
fuente
r'[^' + re.escape(string.printable) + r']'
. (No creo quere.escape()
sea del todo correcto aquí, pero si funciona ...)Lo mejor que se me ocurrió ahora es (gracias a los python-izers anteriores)
def filter_non_printable(str): return ''.join([c for c in str if ord(c) > 31 or ord(c) == 9])
Esta es la única forma en que he descubierto que funciona con caracteres / cadenas Unicode
¿Alguna mejor opción?
fuente
El de abajo funciona más rápido que los de arriba. Echar un vistazo
''.join([x if x in string.printable else '' for x in Str])
fuente
"".join([c if 0x21<=ord(c) and ord(c)<=0x7e else "" for c in ss])
Hay cuando se usa la
regex
biblioteca: https://pypi.org/project/regex/Está bien mantenido y es compatible con expresiones regulares Unicode, expresiones regulares Posix y muchos más. El uso (firmas de métodos) es muy similar al de Python
re
.De la documentación:
(No estoy afiliado, solo un usuario).
fuente
Según la respuesta de @ Ber, sugiero eliminar solo los caracteres de control como se define en las categorías de la base de datos de caracteres Unicode :
import unicodedata def filter_non_printable(s): return ''.join(c for c in s if not unicodedata.category(c).startswith('C'))
fuente
startswith('C')
pero esto fue mucho menos eficaz en mis pruebas que cualquier otra solución.if unicodedata.category(c)[0] != 'C'
en su lugar. ¿Funciona mejor? Si prefiere la velocidad de ejecución sobre los requisitos de memoria, puede precalcular la tabla como se muestra en stackoverflow.com/a/93029/3779655Para eliminar los 'espacios en blanco',
import re t = """ \n\t<p> </p>\n\t<p> </p>\n\t<p> </p>\n\t<p> </p>\n\t<p> """ pat = re.compile(r'[\t\n]') print(pat.sub("", t))
fuente
Adaptado de las respuestas de Ants Aasma y shawnrad :
nonprintable = set(map(chr, list(range(0,32)) + list(range(127,160)))) ord_dict = {ord(character):None for character in nonprintable} def filter_nonprintable(text): return text.translate(ord_dict) #use str = "this is my string" str = filter_nonprintable(str) print(str)
probado en Python 3.7.7
fuente