Лучший способ убрать знаки препинания из строки

Похоже, должен быть способ попроще, чем:

import string
s = "string. With. Punctuation?" # Sample string 
out = s.translate(string.maketrans("",""), string.punctuation)

Здесь?

Мне это кажется довольно простым. Почему вы хотите это изменить? Если вы хотите, чтобы было проще, просто оберните то, что вы только что написали, в функцию.

Hannes Ovrén 05.11.2008 20:38

Что ж, это просто казалось хакерским - использовать побочный эффект str.translate для выполнения работы. Я думал, что может быть что-то вроде str.strip (chars), которое работает со всей строкой, а не только с границами, которые я пропустил.

Lawrence Johnston 05.11.2008 21:00

Зависит и от данных. Использование этого для данных, где есть имена серверов с подчеркиванием как часть имени (довольно часто в некоторых местах), может быть плохим. Просто убедитесь, что вы знаете данные и их содержание, иначе вы можете столкнуться с подмножеством проблемы clbuttic.

EBGreen 05.11.2008 21:10

Зависит также от того, что вы называете пунктуацией. "The temperature in the O'Reilly & Arbuthnot-Smythe server's main rack is 40.5 degrees." содержит ровно ОДИН знак препинания, второй "."

John Machin 09.03.2010 00:49

Я удивлен, что никто не упомянул, что string.punctuation вообще не включает неанглийскую пунктуацию. Я думаю о。 ,!? : × «» 〟и так далее.

Clément 03.01.2013 19:40

Не работает со строкой юникода?

Sergey Orshanskiy 28.02.2015 07:25

@JohnMachin, ты забываешь, что ' ' - знаки препинания.

Wayne Werner 01.05.2017 19:42

Начиная с python 3.1 (как минимум до 3.8.3) вам понадобится: str.maketrans("","", string.punctuation) на эта документация с изменением задокументировано в 3.1

Brownbat 02.05.2020 19:16

Большая часть обсуждения здесь - это Python 2, этот вопрос похож, но есть превосходные ответы Python 3.

David Jones 10.11.2020 14:13
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
711
9
888 654
27
Перейти к ответу Данный вопрос помечен как решенный

Ответы 27

Не обязательно проще, но по-другому, если вы больше знакомы с семьей re.

import re, string
s = "string. With. Punctuation?" # Sample string 
out = re.sub('[%s]' % re.escape(string.punctuation), '', s)

Работает, потому что строка string.punctuation имеет последовательность, -. в правильном, возрастающем, без пробелов, порядке ASCII. Хотя Python имеет это право, когда вы пытаетесь использовать подмножество string.punctuation, это может стать препятствием для показа из-за неожиданности «-».

S.Lott 05.11.2008 20:49

На самом деле это все еще неправильно. Последовательность «\]» обрабатывается как escape (по совпадению не закрывающая], чтобы избежать другого сбоя), но оставляет \ без экранирования. Вы должны использовать re.escape (string.punctuation), чтобы предотвратить это.

Brian 05.11.2008 21:15

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

Vinko Vrsalovic 06.11.2008 02:21

Обычно я использую что-то вроде этого:

>>> s = "string. With. Punctuation?" # Sample string
>>> import string
>>> for c in string.punctuation:
...     s= s.replace(c,"")
...
>>> s
'string With Punctuation'

Углифицированный однострочный файл: reduce(lambda s,c: s.replace(c, ''), string.punctuation, s).

jfs 11.08.2012 16:03

отлично, но не устраняет некоторые знаки препинания, такие как длинный дефис

Vladimir Stazhilov 17.01.2015 18:57
Ответ принят как подходящий

С точки зрения эффективности вы не победите

s.translate(None, string.punctuation)

Для более высоких версий Python используйте следующий код:

s.translate(str.maketrans('', '', string.punctuation))

Он выполняет операции с необработанными строками на C с помощью таблицы поиска - мало что может превзойти это, кроме написания собственного кода C.

Если скорость не вызывает беспокойства, есть еще один вариант:

exclude = set(string.punctuation)
s = ''.join(ch for ch in s if ch not in exclude)

Это быстрее, чем s.replace с каждым символом, но не будет работать так же хорошо, как подходы, не являющиеся чистыми python, такие как регулярные выражения или string.translate, как вы можете видеть из приведенных ниже значений времени. Для этого типа проблемы окупается выполнение ее на как можно более низком уровне.

Код времени:

import re, string, timeit

s = "string. With. Punctuation"
exclude = set(string.punctuation)
table = string.maketrans("","")
regex = re.compile('[%s]' % re.escape(string.punctuation))

def test_set(s):
    return ''.join(ch for ch in s if ch not in exclude)

def test_re(s):  # From Vinko's solution, with fix.
    return regex.sub('', s)

def test_trans(s):
    return s.translate(table, string.punctuation)

def test_repl(s):  # From S.Lott's solution
    for c in string.punctuation:
        s=s.replace(c,"")
    return s

print "sets      :",timeit.Timer('f(s)', 'from __main__ import s,test_set as f').timeit(1000000)
print "regex     :",timeit.Timer('f(s)', 'from __main__ import s,test_re as f').timeit(1000000)
print "translate :",timeit.Timer('f(s)', 'from __main__ import s,test_trans as f').timeit(1000000)
print "replace   :",timeit.Timer('f(s)', 'from __main__ import s,test_repl as f').timeit(1000000)

Это дает следующие результаты:

sets      : 19.8566138744
regex     : 6.86155414581
translate : 2.12455511093
replace   : 28.4436721802

Спасибо за информацию о времени, я сам думал о том, чтобы сделать что-то подобное, но твое написано лучше, чем все, что я сделал бы, и теперь я могу использовать его в качестве шаблона для любого будущего кода времени, который я хочу написать :).

Lawrence Johnston 05.11.2008 22:57

Отличный ответ. Вы можете упростить его, убрав таблицу. В документах говорится: «установите для аргумента таблицы значение Нет для переводов, которые удаляют только символы» (docs.python.org/library/stdtypes.html#str.translate)

Alexandros Marinos 02.07.2011 01:24

Использование списка для ''.join() сделало бы его немного быстрее, но недостаточно быстро, чтобы превзойти regex или translate. См. понимание списка без [], Python, почему это так.

Martijn Pieters 08.11.2013 16:13

Также стоит отметить, что translate () ведет себя по-разному для объектов str и unicode, поэтому вы должны быть уверены, что всегда работаете с одним и тем же типом данных, но подход в этом ответе одинаково хорошо работает для обоих, что удобно.

Richard J 16.01.2015 12:35

В Python3 следует заменить table = string.maketrans("","") на table = str.maketrans({key: None for key in string.punctuation})?

SparkAndShine 14.05.2016 02:36

С какой целью делать set(string.punctuation)? У него есть только уникальные значения.

mlissner 08.08.2016 18:03

@mlissner - эффективность. Если это список / строка, вам нужно выполнить линейное сканирование, чтобы узнать, находится ли буква в строке. Однако с набором или словарем он обычно будет быстрее (за исключением действительно маленьких строк), поскольку ему не нужно проверять каждое значение.

Brian 27.09.2016 17:13

@sparkandshine Да, за исключением того, что вам нужно сопоставить порядковые номера каждого ключа символу замены, поэтому в Python 3 это будет s.translate({ord(c): None for c in string.punctuation}).

Galen Long 26.01.2017 19:49

Чтобы обновить обсуждение, начиная с Python 3.6, regex теперь является наиболее эффективным методом! Это почти в 2 раза быстрее, чем переводчик. Кроме того, наборы и замены уже не так уж и плохи! Оба они улучшены более чем в 4 раза :)

Ryan Soklaski 25.07.2017 01:35

В Python 3 таблицу перевода также можно создать с помощью table = str.maketrans('', '', string.punctuation)docs.python.org/3/library/stdtypes.html#str.maketrans

RyanLeiTaiwan 18.03.2018 19:11

Большое спасибо за временную сложность каждого подхода.

Sundeep Pidugu 22.07.2019 11:49

>>> s.translate (None, string.punctuation) Traceback (последний вызов последним): файл «<stdin>», строка 1, в <module> TypeError: str.translate () принимает ровно один аргумент (задано 2) в python3, я думаю, этот ответ нуждается в обновлении

John D 09.01.2021 02:17

Спасибо за тонну информации. Полезно

Gaurav Koradiya 25.03.2021 17:18
myString.translate(None, string.punctuation)

ах, я пробовал это, но это работает не во всех случаях. myString.translate (string.maketrans ("", ""), string.punctuation) работает нормально.

Aidan Kane 12.08.2010 16:30

Обратите внимание, что для str в Python 3 и unicode в Python 2 аргумент deletechars не поддерживается.

agf 14.04.2012 04:36

@agf: вы все еще можете используйте .translate() для удаления знаков препинания даже в случаях Unicode и py3k, используя аргумент словаря.

jfs 28.08.2012 11:53

myString.translate (string.maketrans ("", ""), string.punctuation) НЕ будет работать со строками Unicode (выяснилось на собственном опыте)

Marc Maxmeister 25.07.2014 23:25

@MarcMaxson: myString.translate(str.maketrans("", "", string.punctuation)) действительно работает для строк Unicode на Python 3. Хотя string.punctuation включает только знаки препинания ascii. Щелкните ссылка в моем предыдущем комментарии. Он показывает, как удалить все знаки препинания (включая Unicode).

jfs 01.10.2014 12:56
TypeError: translate() takes exactly one argument (2 given) :(
Brian Tingle 23.04.2015 21:35

@BrianTingle: посмотрите код Python 3 в моем комментарии (он передает один аргумент). Перейдите по ссылке, чтобы увидеть код Python 2, который работает с юникодом. и его адаптация Python 3

jfs 15.07.2015 20:21

Возможно, это не лучшее решение, но я сделал это так.

import string
f = lambda x: ''.join([i for i in x if i not in string.punctuation])

string.punctuation - это ASCII Только! Более правильный (но и гораздо более медленный) способ - использовать модуль unicodedata:

# -*- coding: utf-8 -*-
from unicodedata import category
s = u'String — with -  «punctation »...'
s = ''.join(ch for ch in s if category(ch)[0] != 'P')
print 'stripped', s

Вы также можете обобщить и исключить другие типы символов:

''.join(ch for ch in s if category(ch)[0] not in 'SP')

Он также удалит такие символы, как ~*+§$, которые могут быть или не быть «пунктуацией» в зависимости от точки зрения.

Вы могли: regex.sub(ur"\p{P}+", "", text)

jfs 11.08.2012 16:07

К сожалению, такие вещи, как ~, не относятся к категории знаков препинания. Вам также необходимо проверить категорию символов.

C.J. Jackson 06.10.2019 07:08

Мне нравится использовать такую ​​функцию:

def scrub(abc):
    while abc[-1] is in list(string.punctuation):
        abc=abc[:-1]
    while abc[0] is in list(string.punctuation):
        abc=abc[1:]
    return abc

Это удаление символов с начала и с конца; используйте для этого abc.strip(string.punctuation). Он не удалит такие символы в центре.

Martijn Pieters 09.02.2016 00:13

Регулярные выражения достаточно просты, если вы их знаете.

import re
s = "string. With. Punctuation?"
s = re.sub(r'[^\w\s]','',s)

@Outlier Explanation: заменяет символы слова not (^) или пробелы пустой строкой. Однако будьте осторожны, например, \ w слишком часто совпадает с подчеркиванием.

Matthias 03.02.2016 18:28

@SIslam Я думаю, он будет работать с юникодом с установленным флагом юникода, то есть s = re.sub(r'[^\w\s]','',s, re.UNICODE). Тестируя его с помощью python 3 в Linux, он работает даже без флага с тамильскими буквами தமிழ்.

Matthias 03.02.2016 18:31

@Matthias Я пробовал код с Python 3.6.5 на Mac, вывод тамильских букв выглядит немного иначе, ввод தமிழ் становится தமழ. Я ничего не знаю о тамильском, не уверен, что это ожидается.

shiouming 28.05.2019 05:45

@Matthias Он путается с границами слов при работе с UNICODE бенгальским текстом и дает неправильные слова независимо от того, используется флаг UNICODE или нет.

hafiz031 09.09.2020 16:04

Для значений Python 3 str или Python 2 unicodestr.translate() принимает только словарь; кодовые точки (целые числа) ищутся в этом сопоставлении, и все, что сопоставлено с None, удаляется.

Чтобы удалить (некоторые?) Знаки препинания, используйте:

import string

remove_punct_map = dict.fromkeys(map(ord, string.punctuation))
s.translate(remove_punct_map)

dict.fromkeys() метод класса упрощает создание сопоставления, устанавливая все значения на None на основе последовательности ключей.

Чтобы удалить знаки препинания все, а не только знаки препинания ASCII, ваша таблица должна быть немного больше; см. Ответ Дж. Ф. Себастьяна (версия Python 3):

import unicodedata
import sys

remove_punct_map = dict.fromkeys(i for i in range(sys.maxunicode)
                                 if unicodedata.category(chr(i)).startswith('P'))

Для поддержки Unicode string.punctuation недостаточно. См. мой ответ

jfs 01.10.2014 00:57

@ Дж. Ф. Себастьян: действительно, в моем ответе использовались те же символы, что и в голосе, получившем наибольшее количество голосов. Добавлена ​​версия вашей таблицы для Python 3.

Martijn Pieters 01.10.2014 01:08

ответ, получивший наибольшее количество голосов, работает только для строк ascii. Ваш ответ явно заявляет о поддержке Unicode.

jfs 01.10.2014 12:53

@ J.F.Sebastian: он работает для строк Unicode. Он удаляет знаки препинания ASCII. Я никогда не утверждал, что он удаляет знаки препинания все. :-) Суть в том, чтобы предоставить правильную технику для объектов unicode по сравнению с объектами Python 2 str.

Martijn Pieters 01.10.2014 13:02

Вот функция, которую я написал. Это не очень эффективно, но это просто, и вы можете добавлять или удалять любые знаки препинания, которые захотите:

def stripPunc(wordList):
    """Strips punctuation from list of words"""
    puncList = [".",";",":","!","?","/","\\",",","#","@","$","&",")","(","\""]
    for punc in puncList:
        for word in wordList:
            wordList=[word.replace(punc,'') for word in wordList]
    return wordList

Однострочник может быть полезен в не очень строгих случаях:

''.join([c for c in s if c.isalnum() or c.isspace()])

Вот однострочный текст для Python 3.5:

import string
"l*ots! o(f. p@u)n[c}t]u[a'ti\"on#$^?/".translate(str.maketrans({a:None for a in string.punctuation}))

Для удобства использования я резюмирую примечание о разделении знаков препинания в строке как в Python 2, так и в Python 3. Подробное описание см. В других ответах.


Python 2

import string

s = "string. With. Punctuation?"
table = string.maketrans("","")
new_s = s.translate(table, string.punctuation)      # Output: string without punctuation

Python 3

import string

s = "string. With. Punctuation?"
table = str.maketrans(dict.fromkeys(string.punctuation))  # OR {key: None for key in string.punctuation}
new_s = s.translate(table)                          # Output: string without punctuation

Я еще не видел этого ответа. Просто используйте регулярное выражение; он удаляет все символы, кроме символов слова (\w) и цифровых символов (\d), за которыми следует пробельный символ (\s):

import re
s = "string. With. Punctuation?" # Sample string 
out = re.sub(ur'[^\w\d\s]+', '', s)
\d является избыточным, поскольку он является подмножеством \w.
blhsing 10.01.2019 21:37

Числовые символы считаются подмножеством символов Word? Я думал, что символ Word - это любой символ, который может составить настоящее слово, например а-я-я?

Blairg23 10.01.2019 23:55

Да, «слово» в регулярном выражении включает буквы, числа и подчеркивание. См. Описание \w в документации: docs.python.org/3/library/re.html

blhsing 11.01.2019 00:10
>>> s = "string. With. Punctuation?"
>>> s = re.sub(r'[^\w\s]','',s)
>>> re.split(r'\s*', s)


['string', 'With', 'Punctuation']

Пожалуйста, отредактируйте с дополнительной информацией. Ответы только на код и "попробуйте это" не приветствуются, потому что они не содержат доступного для поиска контента и не объясняют, почему кто-то должен "попробовать это".

Paritosh 24.08.2016 10:29

string.punctuation пропускает множество знаков препинания, которые обычно используются в реальном мире. Как насчет решения, которое работает с пунктуацией, отличной от ASCII?

import regex
s = u"string. With. Some・Really Weird、Non?ASCII。 「(Punctuation)」?"
remove = regex.compile(ur'[\p{C}|\p{M}|\p{P}|\p{S}|\p{Z}]+', regex.UNICODE)
remove.sub(u" ", s).strip()

Лично я считаю, что это лучший способ удалить знаки препинания из строки в Python, потому что:

  • Удаляет все знаки препинания Unicode.
  • Его легко изменить, например вы можете удалить \{S}, если хотите убрать знаки препинания, но оставить символы вроде $.
  • Вы можете четко указать, что вы хотите сохранить и что хотите удалить, например, \{Pd} удалит только тире.
  • Это регулярное выражение также нормализует пробелы. Он отображает табуляцию, возврат каретки и другие странности в красивые одиночные пробелы.

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

Эта строчка на самом деле не работает: remove = regex.compile(ur'[\p{C}|\p{M}|\p{P}|\p{S}|\p{Z}]+', regex.UNICODE)

John Stud 29.06.2020 03:09

Вот решение без регулярного выражения.

import string

input_text = "!where??and!!or$$then:)"
punctuation_replacer = string.maketrans(string.punctuation, ' '*len(string.punctuation))    
print ' '.join(input_text.translate(punctuation_replacer).split()).strip()

Output>> where and or then
  • Заменяет знаки препинания пробелами
  • Замените несколько пробелов между словами одним пробелом
  • Удалите конечные пробелы, если они есть, с помощью полоска()
#FIRST METHOD
#Storing all punctuations in a variable    
punctuation='!?,.:;"\')(_-'
newstring='' #Creating empty string
word=raw_input("Enter string: ")
for i in word:
     if (i not in punctuation):
                  newstring+=i
print "The string without punctuation is",newstring

#SECOND METHOD
word=raw_input("Enter string: ")
punctuation='!?,.:;"\')(_-'
newstring=word.translate(None,punctuation)
print "The string without punctuation is",newstring


#Output for both methods
Enter string: hello! welcome -to_python(programming.language)??,
The string without punctuation is: hello welcome topythonprogramminglanguage
with open('one.txt','r')as myFile:

    str1=myFile.read()

    print(str1)


    punctuation = ['(', ')', '?', ':', ';', ',', '.', '!', '/', '"', "'"] 

for i in punctuation:

        str1 = str1.replace(i," ") 
        myList=[]
        myList.extend(str1.split(" "))
print (str1) 
for i in myList:

    print(i,end='\n')
    print ("____________")

Удалите стоп-слова из текстового файла с помощью Python

print('====THIS IS HOW TO REMOVE STOP WORS====')

with open('one.txt','r')as myFile:

    str1=myFile.read()

    stop_words  = "not", "is", "it", "By","between","This","By","A","when","And","up","Then","was","by","It","If","can","an","he","This","or","And","a","i","it","am","at","on","in","of","to","is","so","too","my","the","and","but","are","very","here","even","from","them","then","than","this","that","though","be","But","these"

    myList=[]

    myList.extend(str1.split(" "))

    for i in myList:

        if i not in stop_words:

            print ("____________")

            print(i,end='\n')
import re
s = "string. With. Punctuation?" # Sample string 
out = re.sub(r'[^a-zA-Z0-9\s]', '', s)

Похоже, это будет работать только для символов ASCII.

SearchTools-Avi 14.10.2019 22:15

В качестве обновления я переписал пример @Brian в Python 3 и внес в него изменения, чтобы переместить этап компиляции регулярного выражения внутри функции. Моя мысль здесь заключалась в том, чтобы рассчитать каждый шаг, необходимый для работы функции. Возможно, вы используете распределенные вычисления и не можете разделять объект регулярного выражения между вашими рабочими, и вам нужно иметь шаг re.compile для каждого рабочего. Кроме того, мне было любопытно примерить две разные реализации maketrans для Python 3.

table = str.maketrans({key: None for key in string.punctuation})

против

table = str.maketrans('', '', string.punctuation)

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

Это полный код:

import re, string, timeit

s = "string. With. Punctuation"


def test_set(s):
    exclude = set(string.punctuation)
    return ''.join(ch for ch in s if ch not in exclude)


def test_set2(s):
    _punctuation = set(string.punctuation)
    for punct in set(s).intersection(_punctuation):
        s = s.replace(punct, ' ')
    return ' '.join(s.split())


def test_re(s):  # From Vinko's solution, with fix.
    regex = re.compile('[%s]' % re.escape(string.punctuation))
    return regex.sub('', s)


def test_trans(s):
    table = str.maketrans({key: None for key in string.punctuation})
    return s.translate(table)


def test_trans2(s):
    table = str.maketrans('', '', string.punctuation)
    return(s.translate(table))


def test_repl(s):  # From S.Lott's solution
    for c in string.punctuation:
        s=s.replace(c,"")
    return s


print("sets      :",timeit.Timer('f(s)', 'from __main__ import s,test_set as f').timeit(1000000))
print("sets2      :",timeit.Timer('f(s)', 'from __main__ import s,test_set2 as f').timeit(1000000))
print("regex     :",timeit.Timer('f(s)', 'from __main__ import s,test_re as f').timeit(1000000))
print("translate :",timeit.Timer('f(s)', 'from __main__ import s,test_trans as f').timeit(1000000))
print("translate2 :",timeit.Timer('f(s)', 'from __main__ import s,test_trans2 as f').timeit(1000000))
print("replace   :",timeit.Timer('f(s)', 'from __main__ import s,test_repl as f').timeit(1000000))

Это мои результаты:

sets      : 3.1830138750374317
sets2      : 2.189873124472797
regex     : 7.142953420989215
translate : 4.243278483860195
translate2 : 2.427158243022859
replace   : 4.579746678471565

Почему никто из вас этим не пользуется?

 ''.join(filter(str.isalnum, s)) 

Слишком медленно?

Обратите внимание, что это также удалит пробелы.

Georgy 29.07.2019 15:36

Учитывая юникод. Код проверен в python3.

from unicodedata import category
text = 'hi, how are you?'
text_without_punc = ''.join(ch for ch in text if not category(ch).startswith('P'))

Вот еще один простой способ сделать это с помощью RegEx

import re

punct = re.compile(r'(\w+)')

sentence = 'This ! is : a # sample $ sentence.' # Text with punctuation
tokenized = [m.group() for m in punct.finditer(sentence)]
sentence = ' '.join(tokenized)
print(sentence) 
'This is a sample sentence'

Попробуйте это :)

regex.sub(r'\p{P}','', s)

Я искал действительно простое решение. вот что у меня получилось:

import re 

s = "string. With. Punctuation?" 
s = re.sub(r'[\W\s]', ' ', s)

print(s)
'string  With  Punctuation '

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