2008-09-18 22 views
67

que utilizo para ejecutardesprendimiento caracteres no imprimibles de una cadena en Python

$s =~ s/[^[:print:]]//g; 

en Perl para deshacerse de los caracteres no imprimibles.

En Python no hay clases de expresiones regulares POSIX, y no puedo escribir [: imprimir:] que signifique lo que quiero. No sé de ninguna manera en Python para detectar si un personaje es imprimible o no.

¿Qué harías?

EDITAR: También debe admitir caracteres Unicode. El modo string.printable los eliminará alegremente de la salida. curses.ascii.isprint devolverá falso para cualquier carácter Unicode.

Respuesta

62

interactuando sobre cadenas es por desgracia bastante lento en Python. Las expresiones regulares son más de un orden de magnitud más rápidas 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 Unicode Character Database para obtener descripciones de las categorías.

import unicodedata, re 

all_chars = (unichr(i) for i in xrange(0x110000)) 
control_chars = ''.join(c for c in all_chars if unicodedata.category(c) == 'Cc') 
# or equivalently and much more efficiently 
control_chars = ''.join(map(unichr, range(0,32) + range(127,160))) 

control_char_re = re.compile('[%s]' % re.escape(control_chars)) 

def remove_control_chars(s): 
    return control_char_re.sub('', s) 
+3

¿Hay suficiente 'Cc' aquí? No sé, solo estoy preguntando: me parece que algunas de las otras categorías 'C' también pueden ser candidatas para este filtro. –

+0

Este código no funciona en 2.6 o 3.2, ¿en qué versión se ejecuta? – Seth

+0

@Seth: funciona para mí, Ubuntu 10.04, Python 2.6.5. – tripleee

1

El mejor que he encontrado hasta ahora es con (gracias a la pitón-izers arriba)

def filter_non_printable(str): 
    return ''.join([c for c in str if ord(c) > 31 or ord(c) == 9]) 

Ésta es la única forma que he encontrado que funciona con caracteres Unicode/Cuerdas

¿Alguna mejor opción?

+1

A menos que esté en python 2.3, los internos [] s son redundantes. "return '' .join (c por c ...)" – habnabit

+0

No es muy redundante, tienen diferentes significados (y características de rendimiento), aunque el resultado final es el mismo. – Miles

+0

¿El otro extremo del rango no debe protegerse también?: "ord (c) <= 126" –

51

Por lo que sé, el método más Pythonic/eficiente sería:

import string 

filtered_string = filter(lambda x: x in string.printable, myStr) 
+6

Es posible que desee filtered_string = '' .join (filtro (lambda x:. X en string.printable, myStr) para que regrese una cadena –

+8

Lamentablemente string.printable no contiene caracteres Unicode, y por lo tanto ü o - no estará en la salida ... ¿tal vez haya algo más? –

+13

Debería estar usando una lista de comprensión o expresiones del generador, no filter + lambda. Una de ellas será el 99.9% del tiempo más rápida. '' .join (s para s en myStr si s en string.printable) – habnabit

5

Esta función utiliza listas por comprensión y str.join, 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)) 
+4

isprint tampoco está consciente de unicode:/ –

+2

'filter (isprint, input)' – yingted

8

Usted podría intentar la creación de un filtro utilizando la función unicodedata.category():

printable = Set('Lu', 'Ll', ...) 
def filter_non_printable(str): 
    return ''.join(c for c in str if unicodedata.category(c) in printable) 

Véase el Unicode database character properties de las categorías disponibles

+0

comenzó una comprensión de lista que no terminó en yo tu linea final Sugiero que elimines por completo el soporte de apertura. – tzot

+0

Gracias por señalar esto. Edité la publicación en consecuencia – Ber

+0

Este parece ser el método más directo y directo. Gracias. – dotancohen

8

En Python 3,

def filter_nonprintable(text): 
    import string 
    # Get the difference of all ASCII characters from the set of printable characters 
    nonprintable = set([chr(i) for i in range(128)]).difference(string.printable) 
    # Use translate to remove all non-printable characters 
    return text.translate({ord(character):None for character in nonprintable}) 

Ver this StackOverflow post on removing punctuation por cómo .translate() se compara con la expresión regular & .replace()

0

eliminar 'espacio en blanco',

import re 
t = """ 
\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p> 
""" 
pat = re.compile(r'[\t\n]') 
print(pat.sub("", t)) 
0

la de abajo realiza más rápido que los demás por encima. Eche un vistazo

''.join([x if x in string.printable else '' for x in Str]) 
Cuestiones relacionadas