Iterowanie po łańcuchach jest niestety raczej powolne w Pythonie. Wyrażenia regularne są o rząd wielkości szybsze w tego rodzaju sytuacjach. Musisz tylko sam zbudować klasę postaci. Unicodedata moduł jest bardzo pomocne w tym, w szczególności unicodedata.category () funkcja. Aby zapoznać się z opisami kategorii, zobacz Baza danych znaków Unicode .
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)
W przypadku 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)
W niektórych przypadkach preferowane mogą być dodatkowe kategorie (np. Wszystkie z grupy kontrolnej , chociaż może to spowolnić czas przetwarzania i znacznie zwiększyć użycie pamięci. Liczba znaków na kategorię:
Cc
(kontrola): 65
Cf
(format): 161
Cs
(surogat): 2048
Co
(do użytku prywatnego): 137468
Cn
(nieprzypisane): 836601
Edytuj Dodawanie sugestii z komentarzy.
all_chars = (unichr(i) for i in xrange(sys.maxunicode))
aby uniknąć wąskiego błędu kompilacji.control_chars == '\x00-\x1f\x7f-\x9f'
(testowane na Pythonie 3.5.2)O ile wiem, najbardziej pythonowa / wydajna metoda to:
import string filtered_string = filter(lambda x: x in string.printable, myStr)
źródło
Możesz spróbować skonfigurować filtr za pomocą
unicodedata.category()
funkcji:import unicodedata printable = {'Lu', 'Ll'} def filter_non_printable(str): return ''.join(c for c in str if unicodedata.category(c) in printable)
Informacje na temat dostępnych kategorii zawiera Tabela 4-9 na stronie 175, opisująca właściwości znaków bazy danych Unicode
źródło
printable = {'Lu', 'Ll', Zs', 'Nd'}
W Pythonie 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})
Zobacz ten post StackOverflow na temat usuwania znaków interpunkcyjnych, aby dowiedzieć się, jak .translate () porównuje się z wyrażeniem regularnym i .replace ()
Zakresy można generować za
nonprintable = (ord(c) for c in (chr(i) for i in range(sys.maxunicode)) if unicodedata.category(c)=='Cc')
pomocą kategorii bazy danych znaków Unicode, jak pokazano na @Ants Aasma.źródło
text.translate({c:None for c in itertools.chain(range(0x00,0x20),range(0x7f,0xa0))})
.Poniższe będzie działać z wejściem Unicode i jest dość szybkie ...
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('') == ''
Moje własne testy sugerują, że to podejście jest szybsze niż funkcje, które iterują po ciągu i zwracają wynik za pomocą
str.join
.źródło
LINE_BREAK_CHARACTERS = set(["\n", "\r"])
iand not chr(i) in LINE_BREAK_CHARACTERS
podczas budowania tabeli.Ta funkcja wykorzystuje wyrażenia listowe i str.join, więc działa w czasie liniowym zamiast O (n ^ 2):
from curses.ascii import isprint def printable(input): return ''.join(char for char in input if isprint(char))
źródło
filter(isprint,input)
Jeszcze inna opcja w Pythonie 3:
re.sub(f'[^{re.escape(string.printable)}]', '', my_string)
źródło
r'[^' + re.escape(string.printable) + r']'
. (Myślę, że niere.escape()
jest to do końca poprawne, ale jeśliNajlepsze, co teraz wymyśliłem, to (dzięki python-izers powyżej)
def filter_non_printable(str): return ''.join([c for c in str if ord(c) > 31 or ord(c) == 9])
To jedyny sposób, w jaki się dowiedziałem, że działa ze znakami / ciągami znaków Unicode
Jakieś lepsze opcje?
źródło
Ten poniżej działa szybciej niż pozostałe powyżej. Spójrz
''.join([x if x in string.printable else '' for x in Str])
źródło
"".join([c if 0x21<=ord(c) and ord(c)<=0x7e else "" for c in ss])
Podczas korzystania z
regex
biblioteki są: https://pypi.org/project/regex/Jest dobrze utrzymany i obsługuje Unicode regex, Posix regex i wiele innych. Użycie (sygnatury metod) jest bardzo podobne do tego w Pythonie
re
.Z dokumentacji:
(Nie jestem zrzeszony, tylko użytkownik).
źródło
Opierając się na odpowiedzi @ Bera, proponuję usunąć tylko znaki sterujące zdefiniowane w kategoriach bazy danych znaków Unicode :
import unicodedata def filter_non_printable(s): return ''.join(c for c in s if not unicodedata.category(c).startswith('C'))
źródło
startswith('C')
ale w moich testach było to znacznie mniej wydajne niż jakiekolwiek inne rozwiązanie.if unicodedata.category(c)[0] != 'C'
zamiast tego. Czy działa lepiej? Jeśli wolisz szybkość wykonywania niż wymagania dotyczące pamięci, możesz wstępnie obliczyć tabelę, jak pokazano na stackoverflow.com/a/93029/3779655Aby usunąć „białe znaki”,
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))
źródło
Na podstawie odpowiedzi Ants Aasma i 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)
przetestowano na Pythonie 3.7.7
źródło