Удаление непечатаемых символов из строки в Python

Я использую для бега

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

на Perl, чтобы избавиться от непечатаемых символов.

В Python нет классов регулярных выражений POSIX, и я не могу написать [: print:], имея в виду то, что я хочу. Я не знаю в Python способа определить, можно ли печатать символ.

Что бы ты сделал?

Обновлено: он также должен поддерживать символы Unicode. Метод string.printable удалит их из вывода. curses.ascii.isprint вернет false для любого символа Юникода.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
94
0
133 923
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Лучшее, что я придумал сейчас, - это (спасибо python-izers выше)

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

Это единственный способ, который, как я выяснил, работает с символами / строками Unicode.

Есть варианты лучше?

Если вы не используете python 2.3, внутренние [] избыточны. "return" .join (c вместо c ...) "

habnabit 19.09.2008 08:08

Не совсем избыточны - они имеют разные значения (и характеристики производительности), хотя конечный результат один и тот же.

Miles 04.06.2009 03:31

Следует ли не защищать и другой конец диапазона ?: "ord (c) <= 126"

Gearoid Murphy 16.03.2011 20:48

Но есть символы Unicode, которые тоже нельзя распечатать.

tripleee 14.08.2012 12:02

Насколько я знаю, наиболее питоническим / эффективным методом будет:

import string

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

Вероятно, вам понадобится filter_string = '' .join (filter (lambda x: x in string.printable, myStr), чтобы вы вернули строку.

Nathan Shively-Sanders 18.09.2008 17:27

К сожалению, string.printable не содержит символов Юникода, и поэтому ü или ó не будет на выходе ... может быть, есть что-то еще?

Vinko Vrsalovic 18.09.2008 17:29

Вы должны использовать понимание списка или выражения генератора, а не фильтр + лямбда. Один из них будет в 99,9% случаев быстрее. '' .join (s вместо s в myStr, если s в string.printable)

habnabit 19.09.2008 02:49

Конечно, многие из вас правы. Я должен перестать пытаться помогать людям, когда они недосыпают!

William Keller 19.09.2008 07:20

@AaronGallagher: на 99,9% быстрее? Откуда вы берете эту фигуру? Сравнение производительности далеко не так плохо.

Chris Morgan 14.01.2012 08:01

Возможно, стоит превратить string.printable в set, прежде чем делать фильтр.

Gareth Rees 12.09.2012 16:25

Привет, Уильям. Кажется, этот метод удаляет все символы, отличные от ASCII. В Unicode есть много печатаемых символов, отличных от ASCII!

dotancohen 11.12.2012 19:28

@ChrisMorgan: Поздний ответ, но утверждают, что это почти всегда будет быстрее, а не то, что это будет намного, намного быстрее.

Oddthinking 31.07.2014 00:10

Имейте в виду: в Python3 фильтр возвращает генератор. Так что либо используйте Nathans ''.join(...), либо str(filter(...)).

marsl 06.04.2018 17:11

Вот моя версия, которая дает представление о том, что было удалено: '' .join ((s if s в строке. Printable else 'X') for s в s_string_to_print)

TaiwanGrapefruitTea 18.10.2018 14:00

Не та табуляция, новая строка и некоторые другие являются частью печатаемых символов. Поэтому, если вы не хотите их включать, вам следует использовать string.printable[:-5].

LoMaPh 12.11.2020 22:38

Эта функция использует понимание списка и 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 (контроль): 65
  • Cf (формат): 161
  • Cs (суррогат): 2048
  • Co (для частного использования): 137468
  • Cn (не назначен): 836601

Редактировать Добавление предложений из комментариев.

Здесь достаточно "Cc"? Я не знаю, я просто спрашиваю - мне кажется, что некоторые другие категории C также могут быть кандидатами на этот фильтр.

Patrick Johnmeyer 18.09.2008 21:10

Этот код не работает в 2.6 или 3.2, в какой версии он работает?

Seth 09.08.2011 07:41

Эта функция, как опубликовано, удаляет половину еврейских символов. Я получаю одинаковый эффект для обоих приведенных методов.

dotancohen 11.12.2012 19:32

С точки зрения производительности, разве в этом случае string.translate () не будет работать быстрее? См. stackoverflow.com/questions/265960/…

Kashyap 04.10.2013 00:19

Это не подходит для «узкой» сборки Python (16-битный Unicode). Это стандартная сборка для Mac. stackoverflow.com/questions/7105874

Edward Falk 14.12.2014 20:03

@ants aasma: пожалуйста, скажите мне, как можно использовать ваш подход к созданию класса символов для подсчета управляющих символов в строке (а не их удаления)? Я не вижу подходящего метода в re.

chrisinmtown 02.04.2015 14:46

@Edward Falk: Для узкой сборки поместите all_chars = (unichr (i) for i в xrange (0x110000) в предложение try, затем то же самое с xrange (0x10000) в разделе except - позволяет ему работать с "Narrow" "сборка (например, OSX)

Dave 23.05.2015 04:01

@PatrickJohnmeyer У тебя есть хорошая мысль, и это меня укусило. Я исправил это, проверив, находится ли unicodedata.category (c) в наборе какой-либо из категорий Unicode 'Other' (см .: fileformat.info/info/unicode/category/index.htm), т.е. set (['Cc', 'Cf', 'Cn', 'Co ',' Cs ']). Обратите внимание, что я использую английские шрифты, поэтому ymmv использую другие шрифты.

Dave 23.05.2015 04:05

Используйте all_chars = (unichr(i) for i in xrange(sys.maxunicode)), чтобы избежать ошибки узкой сборки.

danmichaelo 25.11.2015 00:01

Для меня control_chars == '\x00-\x1f\x7f-\x9f' (проверено на Python 3.5.2)

AXO 09.09.2016 19:03

Могу ли я применить это к фрейму данных pandas, если да, пожалуйста, объясните, как

Wcan 19.10.2017 22:56

На Python3 используйте chr() вместо unichr() и range() вместо xrange(). Кроме того, для комбинации двух итераторов, возвращаемых range(), следует использовать itertools.chain(): itertools.chain(range(), range()). Для удобства чтения я предлагаю использовать шестнадцатеричные числа (спасибо @AXO) в статических диапазонах: range(0x00,0x20) и range(0x7f,0xa0).

darkdragon 24.06.2020 09:43

он по-прежнему хранит коды вроде \xa0

Dima Lituiev 29.12.2020 05:10

Вы можете попробовать настроить фильтр с помощью функции 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 для доступных категорий.

вы начали понимание списка, которое не закончилось вашей последней строкой. Я предлагаю вам полностью удалить открывающую скобу.

tzot 19.09.2008 16:13

Спасибо, что указали на это. Я отредактировал сообщение соответствующим образом

Ber 05.10.2008 19:32

Это кажется наиболее прямым и понятным методом. Спасибо.

dotancohen 21.07.2013 09:34

это должен быть printable = set(['Lu', 'Ll']), не так ли?

Fabrizio Miano 04.04.2019 17:27

@FabrizioMiano Вы правы. Или установите (('Lu', 'Ll')) Thanx

Ber 05.04.2019 16:10

@Ber Ты хотел сказать printable = {'Lu', 'Ll'}?

Csaba Toth 31.05.2019 10:04

@CsabaToth Все три действительны и дают один и тот же набор. Возможно, ваш лучший способ указать литерал набора.

Ber 04.06.2019 12:56

@Ber Все они имеют один и тот же набор, некоторые линтеры советуют вам использовать тот, который я посоветовал.

Csaba Toth 04.06.2019 21:15

но это удаляет пробел в строке. Как сохранить пробел в строке?

Anudocs 06.11.2019 12:51

@AnubhavJhalani Вы можете добавить в фильтр больше категорий Unicode. Для резервирования пробелов и цифр в дополнение к буквам используйте printable = {'Lu', 'Ll', Zs', 'Nd'}.

Ber 07.11.2019 22:41

Предлагаю удалить только управляющие символы. См. Мой ответ для примера.

darkdragon 23.06.2020 11:27

Я обнаружил, что после добавления 'Zs' для включения пробелов этот метод не удалял символ '\xa0', который Python, похоже, не печатает. Очевидно, это «неразрывное пространство». Согласно эта почта вам нужно удалить это вручную, что является проблемой.

Bill 19.01.2021 02:12

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

darkdragon 23.06.2020 11:56

Чтобы удалить "пробел",

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

Собственно и квадратные скобки вам не нужны.

tripleee 29.11.2019 18:16

Тот, что ниже, работает быстрее, чем другие выше. Взглянуть

''.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])

evandrix 24.06.2019 23:55

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 линии. Благодарность

Chop Labalagun 28.06.2019 00:47

по какой-то причине это отлично работает в Windows, но не может использовать его в Linux, мне пришлось изменить f на r, но я не уверен, что это решение.

Chop Labalagun 13.07.2019 03:02

Похоже, ваш Linux Python был слишком стар, чтобы поддерживать f-строки. r-строки совсем другие, хотя можно было бы сказать r'[^' + re.escape(string.printable) + r']'. (Я не думаю, что re.escape() здесь полностью правильный, но если он работает ...)

tripleee 29.11.2019 18:14

К сожалению, string.printable не содержит символов Юникода, и поэтому ü или ó не будет на выходе ...

the_economist 12.02.2021 16:00

Следующее будет работать с вводом 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.

Это единственный ответ, который у меня работает с символами Юникода. Здорово, что вы предоставили тестовые примеры!

pir 12.09.2019 05:02

Если вы хотите разрешить разрывы строк, добавьте LINE_BREAK_CHARACTERS = set(["\n", "\r"]) и and not chr(i) in LINE_BREAK_CHARACTERS при построении таблицы.

pir 12.09.2019 05:03

Адаптировано из ответов Муравьи Аасма и 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'))

Это отличный ответ!

tdc 24.06.2020 01:42

Возможно, вы что-то заметили с startswith('C'), но в моем тестировании он был гораздо менее эффективным, чем любое другое решение.

Big McLargeHuge 08.10.2020 22:13

big-mclargehuge: Целью моего решения было сочетание полноты и простоты / удобочитаемости. Вместо этого вы можете попробовать использовать if unicodedata.category(c)[0] != 'C'. Он работает лучше? Если вы предпочитаете скорость выполнения требованиям к памяти, можно предварительно вычислить таблицу, как показано в stackoverflow.com/a/93029/3779655

darkdragon 11.10.2020 18:25

Другие вопросы по теме