Я использую для бега
$s =~ s/[^[:print:]]//g;
на Perl, чтобы избавиться от непечатаемых символов.
В Python нет классов регулярных выражений POSIX, и я не могу написать [: print:], имея в виду то, что я хочу. Я не знаю в Python способа определить, можно ли печатать символ.
Что бы ты сделал?
Обновлено: он также должен поддерживать символы Unicode. Метод string.printable удалит их из вывода. curses.ascii.isprint вернет false для любого символа Юникода.






Лучшее, что я придумал сейчас, - это (спасибо python-izers выше)
def filter_non_printable(str):
return ''.join([c for c in str if ord(c) > 31 or ord(c) == 9])
Это единственный способ, который, как я выяснил, работает с символами / строками Unicode.
Есть варианты лучше?
Не совсем избыточны - они имеют разные значения (и характеристики производительности), хотя конечный результат один и тот же.
Следует ли не защищать и другой конец диапазона ?: "ord (c) <= 126"
Но есть символы Unicode, которые тоже нельзя распечатать.
Насколько я знаю, наиболее питоническим / эффективным методом будет:
import string
filtered_string = filter(lambda x: x in string.printable, myStr)
Вероятно, вам понадобится filter_string = '' .join (filter (lambda x: x in string.printable, myStr), чтобы вы вернули строку.
К сожалению, string.printable не содержит символов Юникода, и поэтому ü или ó не будет на выходе ... может быть, есть что-то еще?
Вы должны использовать понимание списка или выражения генератора, а не фильтр + лямбда. Один из них будет в 99,9% случаев быстрее. '' .join (s вместо s в myStr, если s в string.printable)
Конечно, многие из вас правы. Я должен перестать пытаться помогать людям, когда они недосыпают!
@AaronGallagher: на 99,9% быстрее? Откуда вы берете эту фигуру? Сравнение производительности далеко не так плохо.
Возможно, стоит превратить string.printable в set, прежде чем делать фильтр.
Привет, Уильям. Кажется, этот метод удаляет все символы, отличные от ASCII. В Unicode есть много печатаемых символов, отличных от ASCII!
@ChrisMorgan: Поздний ответ, но утверждают, что это почти всегда будет быстрее, а не то, что это будет намного, намного быстрее.
Имейте в виду: в Python3 фильтр возвращает генератор. Так что либо используйте Nathans ''.join(...), либо str(filter(...)).
Вот моя версия, которая дает представление о том, что было удалено: '' .join ((s if s в строке. Printable else 'X') for s в s_string_to_print)
Не та табуляция, новая строка и некоторые другие являются частью печатаемых символов. Поэтому, если вы не хотите их включать, вам следует использовать string.printable[:-5].
Эта функция использует понимание списка и str.join, поэтому она выполняется за линейное время вместо O (n ^ 2):
from curses.ascii import isprint
def printable(input):
return ''.join(char for char in input if isprint(char))
К сожалению, перебор строк в Python выполняется довольно медленно. Регулярные выражения для такого рода вещей более чем на порядок быстрее. Вам просто нужно самому создать класс персонажа. Модуль юникодированные данные очень полезен для этого, особенно функция unicodedata.category (). Описание категорий см. В База данных символов Юникода.
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)
# or equivalently and much more efficiently
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)
Для 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)
# or equivalently and much more efficiently
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)
Для некоторых случаев использования дополнительные категории (например, все из группы контроль могут быть предпочтительнее, хотя это может замедлить время обработки и значительно увеличить использование памяти. Количество символов в категории:
Cc (контроль): 65Cf (формат): 161Cs (суррогат): 2048Co (для частного использования): 137468Cn (не назначен): 836601Редактировать Добавление предложений из комментариев.
Здесь достаточно "Cc"? Я не знаю, я просто спрашиваю - мне кажется, что некоторые другие категории C также могут быть кандидатами на этот фильтр.
Этот код не работает в 2.6 или 3.2, в какой версии он работает?
Эта функция, как опубликовано, удаляет половину еврейских символов. Я получаю одинаковый эффект для обоих приведенных методов.
С точки зрения производительности, разве в этом случае string.translate () не будет работать быстрее? См. stackoverflow.com/questions/265960/…
Это не подходит для «узкой» сборки Python (16-битный Unicode). Это стандартная сборка для Mac. stackoverflow.com/questions/7105874
@ants aasma: пожалуйста, скажите мне, как можно использовать ваш подход к созданию класса символов для подсчета управляющих символов в строке (а не их удаления)? Я не вижу подходящего метода в re.
@Edward Falk: Для узкой сборки поместите all_chars = (unichr (i) for i в xrange (0x110000) в предложение try, затем то же самое с xrange (0x10000) в разделе except - позволяет ему работать с "Narrow" "сборка (например, OSX)
@PatrickJohnmeyer У тебя есть хорошая мысль, и это меня укусило. Я исправил это, проверив, находится ли unicodedata.category (c) в наборе какой-либо из категорий Unicode 'Other' (см .: fileformat.info/info/unicode/category/index.htm), т.е. set (['Cc', 'Cf', 'Cn', 'Co ',' Cs ']). Обратите внимание, что я использую английские шрифты, поэтому ymmv использую другие шрифты.
Используйте all_chars = (unichr(i) for i in xrange(sys.maxunicode)), чтобы избежать ошибки узкой сборки.
Для меня control_chars == '\x00-\x1f\x7f-\x9f' (проверено на Python 3.5.2)
Могу ли я применить это к фрейму данных pandas, если да, пожалуйста, объясните, как
На Python3 используйте chr() вместо unichr() и range() вместо xrange(). Кроме того, для комбинации двух итераторов, возвращаемых range(), следует использовать itertools.chain(): itertools.chain(range(), range()). Для удобства чтения я предлагаю использовать шестнадцатеричные числа (спасибо @AXO) в статических диапазонах: range(0x00,0x20) и range(0x7f,0xa0).
он по-прежнему хранит коды вроде \xa0
Вы можете попробовать настроить фильтр с помощью функции unicodedata.category():
import unicodedata
printable = {'Lu', 'Ll'}
def filter_non_printable(str):
return ''.join(c for c in str if unicodedata.category(c) in printable)
См. Таблицу 4-9 на стр. 175 в Свойства символов базы данных Unicode для доступных категорий.
вы начали понимание списка, которое не закончилось вашей последней строкой. Я предлагаю вам полностью удалить открывающую скобу.
Спасибо, что указали на это. Я отредактировал сообщение соответствующим образом
Это кажется наиболее прямым и понятным методом. Спасибо.
это должен быть printable = set(['Lu', 'Ll']), не так ли?
@FabrizioMiano Вы правы. Или установите (('Lu', 'Ll')) Thanx
@Ber Ты хотел сказать printable = {'Lu', 'Ll'}?
@CsabaToth Все три действительны и дают один и тот же набор. Возможно, ваш лучший способ указать литерал набора.
@Ber Все они имеют один и тот же набор, некоторые линтеры советуют вам использовать тот, который я посоветовал.
но это удаляет пробел в строке. Как сохранить пробел в строке?
@AnubhavJhalani Вы можете добавить в фильтр больше категорий Unicode. Для резервирования пробелов и цифр в дополнение к буквам используйте printable = {'Lu', 'Ll', Zs', 'Nd'}.
Предлагаю удалить только управляющие символы. См. Мой ответ для примера.
Я обнаружил, что после добавления 'Zs' для включения пробелов этот метод не удалял символ '\xa0', который Python, похоже, не печатает. Очевидно, это «неразрывное пространство». Согласно эта почта вам нужно удалить это вручную, что является проблемой.
В 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})
См. это сообщение StackOverflow об удалении знаков препинания, чтобы узнать, как .translate () сравнивается с регулярным выражением и .replace ()
Диапазоны могут быть сгенерированы через nonprintable = (ord(c) for c in (chr(i) for i in range(sys.maxunicode)) if unicodedata.category(c)=='Cc') с использованием Категории базы данных символов Юникода, как показано @Ants Aasma.
Было бы лучше использовать диапазоны Unicode (см. Ответ @Ants Aasma). Результатом будет text.translate({c:None for c in itertools.chain(range(0x00,0x20),range(0x7f,0xa0))}).
Чтобы удалить "пробел",
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))
Собственно и квадратные скобки вам не нужны.
Тот, что ниже, работает быстрее, чем другие выше. Взглянуть
''.join([x if x in string.printable else '' for x in Str])
"".join([c if 0x21<=ord(c) and ord(c)<=0x7e else "" for c in ss])
In Python there's no POSIX regex classes
При использовании библиотеки regex есть: https://pypi.org/project/regex/
Он хорошо поддерживается и поддерживает регулярное выражение Unicode, регулярное выражение Posix и многое другое. Использование (сигнатуры методов) очень аналогично re в Python.
Из документации:
[[:alpha:]]; [[:^alpha:]]POSIX character classes are supported. These are normally treated as an alternative form of
\p{...}.
(Я не аффилирован, просто пользователь.)
Еще один вариант в Python 3:
re.sub(f'[^{re.escape(string.printable)}]', '', my_string)
Это отлично сработало для меня и его 1 линии. Благодарность
по какой-то причине это отлично работает в Windows, но не может использовать его в Linux, мне пришлось изменить f на r, но я не уверен, что это решение.
Похоже, ваш Linux Python был слишком стар, чтобы поддерживать f-строки. r-строки совсем другие, хотя можно было бы сказать r'[^' + re.escape(string.printable) + r']'. (Я не думаю, что re.escape() здесь полностью правильный, но если он работает ...)
К сожалению, string.printable не содержит символов Юникода, и поэтому ü или ó не будет на выходе ...
Следующее будет работать с вводом Unicode и довольно быстро ...
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('') == ''
Мое собственное тестирование показывает, что этот подход быстрее, чем функции, которые перебирают строку и возвращают результат с использованием str.join.
Это единственный ответ, который у меня работает с символами Юникода. Здорово, что вы предоставили тестовые примеры!
Если вы хотите разрешить разрывы строк, добавьте LINE_BREAK_CHARACTERS = set(["\n", "\r"]) и and not chr(i) in LINE_BREAK_CHARACTERS при построении таблицы.
Адаптировано из ответов Муравьи Аасма и 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)
протестировано на Python 3.7.7
Основываясь на ответе @Ber, я предлагаю удалить только управляющие символы, как определено в Категории базы данных символов Юникода:
import unicodedata
def filter_non_printable(s):
return ''.join(c for c in s if not unicodedata.category(c).startswith('C'))
Это отличный ответ!
Возможно, вы что-то заметили с startswith('C'), но в моем тестировании он был гораздо менее эффективным, чем любое другое решение.
big-mclargehuge: Целью моего решения было сочетание полноты и простоты / удобочитаемости. Вместо этого вы можете попробовать использовать if unicodedata.category(c)[0] != 'C'. Он работает лучше? Если вы предпочитаете скорость выполнения требованиям к памяти, можно предварительно вычислить таблицу, как показано в stackoverflow.com/a/93029/3779655
Если вы не используете python 2.3, внутренние [] избыточны. "return" .join (c вместо c ...) "