Melhor maneira de retirar a pontuação de uma string

638

Parece que deve haver uma maneira mais simples do que:

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

Existe?

Lawrence Johnston
fonte
3
Parece bem direto para mim. Por que você quer mudar isso? Se você quiser mais fácil, basta agrupar o que você acabou de escrever em uma função.
Hannes Ovrén 5/11/08
2
Bem, parecia meio hackeado estar usando um efeito colateral do str.tradicional para fazer o trabalho. Eu estava pensando que poderia haver algo mais parecido com str.strip (chars) que funcionou em toda a cadeia, em vez de apenas nos limites que eu tinha perdido.
Lawrence Johnston
2
Depende dos dados também. Usar isso em dados nos quais existem nomes de servidor com sublinhados como parte do nome (alguns lugares bastante comuns) pode ser ruim. Apenas certifique-se de conhecer os dados e o que eles contêm ou você pode acabar com um subconjunto do problema de clbuttic.
EBGreen
54
Depende também do que você chama de pontuação. " The temperature in the O'Reilly & Arbuthnot-Smythe server's main rack is 40.5 degrees." contém exatamente UM caractere de pontuação, o segundo "."
precisa
37
Estou surpreso que ninguém tenha mencionado que string.punctuationnão inclui pontuação que não seja do inglês. Estou pensando em : ,!?: × “” 〟e assim por diante.
Clément

Respostas:

929

Do ponto de vista da eficiência, você não vai vencer

s.translate(None, string.punctuation)

Para versões superiores do Python, use o seguinte código:

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

Ele está executando operações de cadeia bruta em C com uma tabela de pesquisa - não há muito que supere isso, mas escrevendo seu próprio código C.

Se a velocidade não é uma preocupação, outra opção é:

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

Isso é mais rápido que o s.replace com cada caractere, mas não terá um desempenho tão bom quanto as abordagens python não puras, como regexes ou string.translate, como você pode ver nos intervalos abaixo. Para esse tipo de problema, fazê-lo no nível mais baixo possível compensa.

Código de tempo:

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)

Isso fornece os seguintes resultados:

sets      : 19.8566138744
regex     : 6.86155414581
translate : 2.12455511093
replace   : 28.4436721802
Brian
fonte
27
Obrigado pela informação de tempo, eu estava pensando em fazer algo assim, mas o seu é melhor escrito do que qualquer coisa que eu teria feito e agora posso usá-lo como modelo para qualquer código de tempo futuro que eu queira escrever :).
31420 John Lawrence Johnston
29
Ótima resposta. Você pode simplificá-lo removendo a tabela. Os médicos dizem: "definir o argumento tabela para Nenhum para traduções que apenas caracteres Delete" ( docs.python.org/library/stdtypes.html#str.translate )
Alexandros Marinos
3
Também é importante notar que o translate () se comporta de maneira diferente nos objetos str e unicode; portanto, você precisa ter certeza de que está sempre trabalhando com o mesmo tipo de dados, mas a abordagem nesta resposta funciona igualmente bem para os dois, o que é útil.
Richard J
36
No Python3, table = string.maketrans("","")deve ser substituído por table = str.maketrans({key: None for key in string.punctuation})?
SparkAndShine
19
Atualizar a discussão, a partir do Python 3.6, regexagora é o método mais eficiente! É quase duas vezes mais rápido que traduzir. Além disso, conjuntos e substituições não são mais tão ruins! Ambos são melhorou em mais de um fator de 4 :)
Ryan Soklaski
143

Expressões regulares são bastante simples, se você as conhece.

import re
s = "string. With. Punctuation?"
s = re.sub(r'[^\w\s]','',s)
Eratóstenes
fonte
4
@ Explicação externa: não substitui (^) caracteres da palavra ou espaços pela sequência vazia. Tenha cuidado, porém, o \ w corresponde também a sublinhado, por exemplo.
Matthias
4
@SIslam Acho que funcionará com unicode com o conjunto de sinalizadores unicode, ou seja s = re.sub(r'[^\w\s]','',s, re.UNICODE). Testando-o com python 3 no linux, ele funciona mesmo sem a bandeira usando letras tamil, தமிழ்.
Matthias
@ Matthias Tentei o código com o Python 3.6.5 no Mac, a saída de letras tâmeis parece um pouco diferente, a entrada becomes் torna-se தமழ. Não tenho conhecimento sobre o tâmil, não tenho certeza se isso é esperado.
Shiouming 28/05/19
71

Para maior comodidade do uso, resumi a nota de pontuação de striping de uma string no Python 2 e no Python 3. Consulte outras respostas para a descrição detalhada.


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
SparkAndShine
fonte
51
myString.translate(None, string.punctuation)
Pyrou
fonte
4
ah, eu tentei isso, mas não funciona em todos os casos. myString.translate (string.maketrans ("", ""), string.punctuation)) funciona bem.
Aidan Kane
12
Observe que, strno Python 3 e unicodeno Python 2, o deletecharsargumento não é suportado.
agf
4
myString.translate (string.maketrans ( "", ""), string.punctuation) não funcionará com strings unicode (descobriu da maneira mais difícil)
Marc Maxmeister
44
TypeError: translate() takes exactly one argument (2 given):(
Brian Tingle
3
@BrianTingle: veja o código Python 3 no meu comentário (ele passa um argumento). Siga o link, para ver Python 2 código que funciona com unicode e sua adaptação Python 3
JFS
29

Eu costumo usar algo como isto:

>>> s = "string. With. Punctuation?" # Sample string
>>> import string
>>> for c in string.punctuation:
...     s= s.replace(c,"")
...
>>> s
'string With Punctuation'
S.Lott
fonte
2
Um fealdade one-liner: reduce(lambda s,c: s.replace(c, ''), string.punctuation, s).
JFS
1
ótimo, no entanto, não remove algumas putuações como hífen mais longo
Vladimir Stazhilov 17/01
25

string.punctuationé apenas ASCII ! Uma maneira mais correta (mas também muito mais lenta) é usar o módulo 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

Você também pode generalizar e remover outros tipos de caracteres:

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

Ele também tira caracteres como os ~*+§$que podem ou não ser "pontuação", dependendo do ponto de vista de alguém.

Björn Lindqvist
fonte
Infelizmente, coisas como ~não fazem parte da categoria de pontuação. Você também precisa testar a categoria Símbolos.
CJ Jackson
24

Não necessariamente mais simples, mas de uma maneira diferente, se você estiver mais familiarizado com a nova família.

import re, string
s = "string. With. Punctuation?" # Sample string 
out = re.sub('[%s]' % re.escape(string.punctuation), '', s)
Vinko Vrsalovic
fonte
1
Funciona porque string.punctuation possui a sequência, -. em ordem ASCII adequada, crescente e sem falhas. Embora o Python tenha esse direito, quando você tenta usar um subconjunto de string.punctuation, ele pode ser um limitador de exibição por causa da surpresa "-".
S.Lott 5/11
2
Na verdade, ainda está errado. A sequência "\]" é tratada como uma fuga (coincidentemente não fecha a], ignorando outra falha), mas deixa \ sem escape. Você deve usar re.escape (string.punctuation) para evitar isso.
Brian
1
Sim, eu o omiti porque funcionou no exemplo para manter as coisas simples, mas você está certo de que deve ser incorporado.
Vinko Vrsalovic 5/11/08
13

Para valores do Python 3 strou Python 2 unicode, str.translate()é necessário apenas um dicionário; codepoints (inteiros) são pesquisados ​​nesse mapeamento e qualquer coisa mapeada para Noneé removida.

Para remover (alguma?) Pontuação, use:

import string

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

O dict.fromkeys()método de classe torna trivial a criação do mapeamento, configurando todos os valores com Nonebase na sequência de chaves.

Para remover toda a pontuação, não apenas a pontuação ASCII, sua tabela precisa ser um pouco maior; veja a resposta de JF Sebastian (versão 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'))
Martijn Pieters
fonte
Para suportar Unicode, string.punctuationnão é suficiente. Veja minha resposta
jfs
@ JFSebastian: na verdade, minha resposta foi usar apenas os mesmos caracteres que o mais votado. Adicionada uma versão Python 3 da sua tabela.
Martijn Pieters
a resposta mais votada funciona apenas para seqüências ascii. Sua resposta reivindica explicitamente o suporte Unicode.
JFS
1
@ JFSebastian: funciona para strings Unicode. Retira a pontuação ASCII. Eu nunca reivindiquei que retira toda a pontuação. :-) O objetivo era fornecer a técnica correta para unicodeobjetos vs. objetos Python 2 str.
Martijn Pieters
12

string.punctuationperde muitos sinais de pontuação que são comumente usados ​​no mundo real. Que tal uma solução que funcione para pontuação não-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()

Pessoalmente, acredito que esta é a melhor maneira de remover pontuação de uma string em Python porque:

  • Remove toda a pontuação Unicode
  • É facilmente modificável, por exemplo, você pode remover o \{S}se quiser remover a pontuação, mas manter símbolos como $.
  • Você pode ser realmente específico sobre o que deseja manter e o que deseja remover, por exemplo \{Pd}, removerá apenas traços.
  • Essa regex também normaliza os espaços em branco. Ele mapeia guias, retornos de carro e outras curiosidades para espaços únicos e agradáveis.

Isso usa propriedades de caracteres Unicode, sobre as quais você pode ler mais na Wikipedia .

Zach
fonte
9

Ainda não vi essa resposta. Basta usar uma regex; remove todos os caracteres, além dos caracteres da palavra ( \w) e dos caracteres numéricos ( \d), seguidos por um caractere de espaço em branco ( \s):

import re
s = "string. With. Punctuation?" # Sample string 
out = re.sub(ur'[^\w\d\s]+', '', s)
Blairg23
fonte
1
\dé redundante, pois é um subconjunto de \w.
blhsing 10/01/19
Caracteres numéricos são considerados um subconjunto de caracteres do Word? Eu pensei que um caractere do Word fosse qualquer caractere que pudesse construir uma palavra real, por exemplo, a-zA-Z?
precisa saber é o seguinte
Sim, uma "palavra" no regex inclui alfabetos, números e sublinhado. Consulte a descrição \wna documentação: docs.python.org/3/library/re.html
blhsing
8

Aqui está uma linha para 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}))
Tim P
fonte
7

Esta pode não ser a melhor solução, mas foi assim que eu fiz.

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

Aqui está uma função que escrevi. Não é muito eficiente, mas é simples e você pode adicionar ou remover qualquer pontuação que desejar:

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
Dr.Tautology
fonte
5
import re
s = "string. With. Punctuation?" # Sample string 
out = re.sub(r'[^a-zA-Z0-9\s]', '', s)
Haythem HADHAB
fonte
Parece que isso funcionaria apenas para caracteres ASCII.
Avirr 14/10/19
5

Apenas como uma atualização, reescrevi o exemplo @Brian no Python 3 e fiz alterações nele para mover a etapa de compilação regex para dentro da função. Meu pensamento aqui era o tempo de cada passo necessário para fazer a função funcionar. Talvez você esteja usando computação distribuída e não possa ter o objeto regex compartilhado entre seus trabalhadores e precise ter re.compileetapas em cada trabalhador. Além disso, eu estava curioso para cronometrar duas implementações diferentes de maketrans para Python 3

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

vs

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

Além disso, adicionei outro método para usar set, onde aproveito a função de interseção para reduzir o número de iterações.

Este é o código completo:

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

Estes são os meus resultados:

sets      : 3.1830138750374317
sets2      : 2.189873124472797
regex     : 7.142953420989215
translate : 4.243278483860195
translate2 : 2.427158243022859
replace   : 4.579746678471565
Krinker
fonte
4
>>> s = "string. With. Punctuation?"
>>> s = re.sub(r'[^\w\s]','',s)
>>> re.split(r'\s*', s)


['string', 'With', 'Punctuation']
Pablo Rodriguez Bertorello
fonte
2
Edite com mais informações. As respostas somente código e "tente isso" são desencorajadas, pois não contêm conteúdo pesquisável e não explicam por que alguém deveria "tentar fazer isso".
Paritosh
4

Aqui está uma solução sem regex.

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
  • Substitui as pontuações por espaços
  • Substitua vários espaços entre as palavras por um único espaço
  • Remova os espaços finais, se houver, com a faixa ()
ngub05
fonte
4

Um one-liner pode ser útil em casos não muito rigorosos:

''.join([c for c in s if c.isalnum() or c.isspace()])
Dom Grey
fonte
2
#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
Animeartistfromhell7
fonte
2
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 ("____________")
Isayas Wakgari Kelbessa
fonte
0

Por que nenhum de vocês usa isso?

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

Muito devagar?

Dehua Li
fonte
Observe que isso também removerá espaços.
Georgy
0

Considerando unicode. Código verificado no 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'))
Rajan saha Raju
fonte
-1

Remova as palavras de parada do arquivo de texto usando 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')
Isayas Wakgari Kelbessa
fonte
-2

Eu gosto de usar uma função como esta:

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
Disco gigante
fonte
1
Isso está removendo caracteres do começo e do fim; use em abc.strip(string.punctuation)vez disso. Não removerá esses caracteres no meio .
Martijn Pieters