web-dev-qa-db-fra.com

Supprimer les caractères non imprimables d'une chaîne en python

J'ai l'habitude de courir

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

sur Perl pour se débarrasser des caractères non imprimables. 

En Python, il n'y a pas de classe POSIX regex, et je ne peux pas écrire [: print:] parce que ça veut dire ce que je veux. Je ne connais aucun moyen en Python de détecter si un caractère est imprimable ou non. 

Qu'est-ce que tu ferais? 

EDIT: Il doit également supporter les caractères Unicode. La méthode string.printable les supprimera volontiers de la sortie. curses.ascii.isprint retournera false pour tout caractère unicode.

75
Vinko Vrsalovic

Itérer sur des chaînes est malheureusement assez lent en Python. Les expressions régulières sont plus rapides d'un ordre de grandeur pour ce genre de chose. Vous devez juste construire la classe de personnage vous-même. Le module unicodedata est très utile pour cela, en particulier la fonction unicodedata.category (). Voir Base de données de caractères Unicode pour une description des catégories.

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)
70
Ants Aasma

Pour autant que je sache, la méthode la plus efficace/pythonique serait:

import string

filtered_string = filter(lambda x: x in string.printable, myStr)
60
William Keller

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})

Voir cet article de StackOverflow sur la suppression de la ponctuation pour savoir comment .translate () se compare à regex & .replace ()

9
shawnrad

Vous pouvez essayer de configurer un filtre en utilisant la fonction unicodedata.category():

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

Voir les propriétés de caractère de la base de données Unicode pour les catégories disponibles.

8
Ber

Cette fonction utilise la compréhension de liste et str.join, elle tourne donc en temps linéaire au lieu de O (n ^ 2)

from curses.ascii import isprint

def printable(input):
    return ''.join(char for char in input if isprint(char))
5
Kirk Strauser

Le meilleur que j'ai trouvé maintenant est (grâce aux python-izers ci-dessus) 

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

C'est la seule façon dont j'ai découvert qui fonctionne avec les caractères/chaînes Unicode

De meilleures options?

2
Vinko Vrsalovic

En Python, il n'y a pas de classes POSIX regex

Si vous utilisez la bibliothèque regex: https://pypi.org/project/regex/

Il est bien entretenu et supporte les expressions régulières Unicode, Posix et bien d’autres. L'utilisation (signatures de méthode) est très similaire à la variable re de Python.

De la documentation:

[[:alpha:]]; [[:^alpha:]]

Les classes de caractères POSIX sont supportées. Celles-ci sont normalement traités comme une forme alternative de \p{...}.

(Je ne suis pas affilié, juste un utilisateur.)

1
Risadinha

Celui ci-dessous fonctionne plus vite que les autres ci-dessus. Regarde

''.join([x if x in string.printable else '' for x in Str])
1
Nilav Baran Ghosh

Pour supprimer les «espaces»,

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
knowingpark

Encore une autre option dans python 3:

re.sub(f'[^{re.escape(string.printable)}]', '', my_string)
0
c6401

Ce qui suit fonctionnera avec l’entrée Unicode et est assez rapide ...

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('') == ''

Mes propres tests suggèrent que cette approche est plus rapide que les fonctions qui parcourent la chaîne et renvoient un résultat en utilisant str.join.

0
ChrisP