La mejor manera de eliminar la puntuación de una cadena

639

Parece que debería haber una forma más simple que:

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

¿Esta ahí?

Lawrence Johnston
fuente
3
Me parece bastante sencillo. por que lo quieres cambiar? Si quieres que sea más fácil, simplemente envuelve lo que acabas de escribir en una función.
Hannes Ovrén
2
Bueno, parecía un poco hacking estar usando un efecto secundario de str.translate para hacer el trabajo. Estaba pensando que podría haber algo más como str.strip (caracteres) que funcionaba en toda la cadena en lugar de solo los límites que me había perdido.
Lawrence Johnston el
2
Depende de los datos también. Usar esto en datos donde hay nombres de servidor con guiones bajos como parte del nombre (bastante común en algunos lugares) podría ser malo. Solo asegúrese de conocer los datos y lo que contienen, o podría terminar con un subconjunto del problema de clbuttic.
EBGreen
54
Depende también de lo que llames puntuación. " The temperature in the O'Reilly & Arbuthnot-Smythe server's main rack is 40.5 degrees." contiene exactamente UN carácter de puntuación, el segundo "."
John Machin
37
Me sorprende que nadie mencione que string.punctuationno incluye puntuación en inglés. Estoy pensando en。 ,!? : × “” 〟, y así sucesivamente.
Clément

Respuestas:

930

Desde una perspectiva de eficiencia, no vas a vencer

s.translate(None, string.punctuation)

Para versiones superiores de Python use el siguiente código:

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

Realiza operaciones de cadena sin formato en C con una tabla de búsqueda: no hay mucho que supere eso, sino escribir su propio código C.

Si la velocidad no es una preocupación, otra opción es:

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

Esto es más rápido que reemplazarlo con cada carácter, pero no funcionará tan bien como los enfoques de Python no puros como regexes o string.translate, como puede ver en los tiempos a continuación. Para este tipo de problema, vale la pena hacerlo al nivel más bajo posible.

Código de tiempo:

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)

Esto da los siguientes resultados:

sets      : 19.8566138744
regex     : 6.86155414581
translate : 2.12455511093
replace   : 28.4436721802
Brian
fuente
27
Gracias por la información sobre el tiempo, estaba pensando en hacer algo así, pero el tuyo está mejor escrito que cualquier cosa que hubiera hecho y ahora puedo usarlo como plantilla para cualquier código de tiempo futuro que quiera escribir :).
Lawrence Johnston el
29
Gran respuesta. Puede simplificarlo quitando la tabla. Los documentos dicen: "establezca el argumento de la tabla en Ninguno para las traducciones que solo eliminan caracteres" ( docs.python.org/library/stdtypes.html#str.translate )
Alexandros Marinos
3
Vale la pena señalar también que translate () se comporta de manera diferente para los objetos str y unicode, por lo que debe asegurarse de que siempre está trabajando con el mismo tipo de datos, pero el enfoque en esta respuesta funciona igualmente bien para ambos, lo cual es útil.
Richard J
36
En Python3, ¿ table = string.maketrans("","")debería reemplazarse con table = str.maketrans({key: None for key in string.punctuation})?
SparkAndShine
19
¡Actualizar la discusión, a partir de Python 3.6, regexes ahora el método más eficiente! Es casi 2 veces más rápido que traducir. Además, ¡los juegos y el reemplazo ya no son tan malos! Ambos están mejorados por más de un factor de 4 :)
Ryan Soklaski
143

Las expresiones regulares son lo suficientemente simples, si las conoce.

import re
s = "string. With. Punctuation?"
s = re.sub(r'[^\w\s]','',s)
Eratóstenes
fuente
44
@Outlier Explicación: reemplaza no caracteres de palabra (^) o espacios con la cadena vacía. Sin embargo, tenga cuidado, el \ w coincide con el guión bajo demasiado generalmente, por ejemplo.
Matthias
44
@SIslam Creo que funcionará con Unicode con el conjunto de banderas Unicode, es decir s = re.sub(r'[^\w\s]','',s, re.UNICODE). Al probarlo con Python 3 en Linux, funciona incluso sin la bandera con letras tamil, தமிழ்.
Matthias
@Matthias Probé el código con Python 3.6.5 en Mac, la salida de letras Tamil se ve un poco diferente, la entrada தமிழ் se convierte en தமழ. No tengo conocimiento sobre Tamil, no estoy seguro de si eso se espera.
Shiouming
71

Para la conveniencia del uso, resumo la nota de puntuación de rayas de una cadena tanto en Python 2 como en Python 3. Consulte otras respuestas para obtener una descripción detallada.


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
Chispa y brillo
fuente
51
myString.translate(None, string.punctuation)
pyrou
fuente
44
Ah, probé esto pero no funciona en todos los casos. myString.translate (string.maketrans ("", ""), string.punctuation) funciona bien.
Aidan Kane el
12
Tenga stren cuenta que para Python 3 y unicodePython 2, el deletecharsargumento no es compatible.
agf
44
myString.translate (string.maketrans ("", ""), string.punctuation) NO funcionará con cadenas Unicode (descubierto de la manera difícil)
Marc Maxmeister
44
TypeError: translate() takes exactly one argument (2 given):(
Brian Tingle
3
@BrianTingle: mira el código de Python 3 en mi comentario (pasa un argumento). Siga el enlace para ver el código de Python 2 que funciona con Unicode y su adaptación de Python 3
jfs
29

Usualmente uso algo como esto:

>>> s = "string. With. Punctuation?" # Sample string
>>> import string
>>> for c in string.punctuation:
...     s= s.replace(c,"")
...
>>> s
'string With Punctuation'
S.Lott
fuente
2
Un afeado de una sola línea: reduce(lambda s,c: s.replace(c, ''), string.punctuation, s).
jfs
1
genial, sin embargo, no elimina algunas fluctuaciones como el guión más largo
Vladimir Stazhilov
25

string.punctuationes solo ASCII ! Una forma más correcta (pero también mucho más lenta) es usar el 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

También puede generalizar y eliminar otros tipos de caracteres:

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

También eliminará caracteres como los ~*+§$que pueden o no ser "puntuación" dependiendo del punto de vista de cada uno.

Björn Lindqvist
fuente
Desafortunadamente, cosas como ~no son parte de la categoría de puntuación. También debe probar la categoría Símbolos.
CJ Jackson
24

No necesariamente más simple, pero de una manera diferente, si está más familiarizado con la familia re.

import re, string
s = "string. With. Punctuation?" # Sample string 
out = re.sub('[%s]' % re.escape(string.punctuation), '', s)
Vinko Vrsalovic
fuente
1
Funciona porque string.punctuation tiene la secuencia, -. en orden apropiado, ascendente, sin espacios, ASCII. Si bien Python tiene este derecho, cuando intenta utilizar un subconjunto de string.punctuation, puede ser un obstáculo para la sorpresa "-".
S.Lott
2
En realidad, todavía está mal. La secuencia "\]" se trata como un escape (casualmente no se cierra el], evitando otro error), pero deja \ sin escapar. Debe usar re.escape (string.punctuation) para evitar esto.
Brian
1
Sí, lo omití porque funcionó para el ejemplo para mantener las cosas simples, pero tiene razón en que debería incorporarse.
Vinko Vrsalovic
13

Para los valores de Python 3 stro Python 2 unicode, str.translate()solo toma un diccionario; los puntos de código (enteros) se buscan en esa asignación y Nonese elimina todo lo asignado .

Para eliminar (¿alguna?) Puntuación, entonces use:

import string

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

El dict.fromkeys()método de clase hace que sea trivial crear la asignación, estableciendo todos los valores en Nonefunción de la secuencia de claves.

Para eliminar toda la puntuación, no solo la puntuación ASCII, su tabla debe ser un poco más grande; ver la respuesta de JF Sebastian (versión 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
fuente
Para soportar Unicode, string.punctuationno es suficiente. Ver mi respuesta
jfs
@JFSebastian: de hecho, mi respuesta fue simplemente usar los mismos caracteres que el más votado. Se agregó una versión de Python 3 de su tabla.
Martijn Pieters
la respuesta mejor votada solo funciona para cadenas ascii. Su respuesta reclama explícitamente el soporte Unicode.
jfs
1
@JFSebastian: funciona para cadenas Unicode. Despoja la puntuación ASCII. Nunca afirmé que elimina todos los signos de puntuación. :-) El objetivo era proporcionar la técnica correcta para los unicodeobjetos frente a los objetos de Python 2 str.
Martijn Pieters
12

string.punctuationpierde montones de signos de puntuación que se usan comúnmente en el mundo real. ¿Qué tal una solución que funciona para la puntuación no 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()

Personalmente, creo que esta es la mejor manera de eliminar la puntuación de una cadena en Python porque:

  • Elimina todos los signos de puntuación Unicode.
  • Es fácilmente modificable, por ejemplo, puede eliminar el \{S}si desea eliminar la puntuación, pero mantener símbolos como $.
  • Puede ser realmente específico sobre lo que desea conservar y lo que desea eliminar, por ejemplo \{Pd}, solo eliminará guiones.
  • Esta expresión regular también normaliza los espacios en blanco. Asigna pestañas, retornos de carro y otras rarezas a espacios agradables y únicos.

Utiliza las propiedades de caracteres Unicode, sobre las que puede leer más en Wikipedia .

Zach
fuente
9

No he visto esta respuesta todavía. Solo usa una expresión regular; elimina todos los caracteres además de los caracteres de palabra ( \w) y los caracteres de número ( \d), seguido de un carácter de espacio en blanco ( \s):

import re
s = "string. With. Punctuation?" # Sample string 
out = re.sub(ur'[^\w\d\s]+', '', s)
Blairg23
fuente
1
\des redundante ya que es un subconjunto de \w.
blhsing
¿Los caracteres numéricos se consideran un subconjunto de caracteres de Word? Pensé que un personaje de Word era cualquier personaje que pudiera construir una palabra real, por ejemplo, a-zA-Z?
Blairg23
Sí, una "palabra" en expresiones regulares incluye alfabetos, números y guiones bajos. Consulte la descripción \wen la documentación: docs.python.org/3/library/re.html
blhsing
8

Aquí hay una línea 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
fuente
7

Esta podría no ser la mejor solución, sin embargo, así es como lo hice.

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

Aquí hay una función que escribí. No es muy eficiente, pero es simple y puede agregar o eliminar cualquier puntuación que desee:

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
fuente
5
import re
s = "string. With. Punctuation?" # Sample string 
out = re.sub(r'[^a-zA-Z0-9\s]', '', s)
Haythem HADHAB
fuente
Parece que eso solo funcionaría para los caracteres ASCII.
avirr
5

Solo como una actualización, reescribí el ejemplo de @Brian en Python 3 e hice cambios para mover el paso de compilación de expresiones regulares dentro de la función. Mi pensamiento aquí era cronometrar cada paso necesario para que la función funcionara. Tal vez está utilizando la informática distribuida y no puede tener un objeto regex compartido entre sus trabajadores y necesita tener un re.compilepaso en cada trabajador. Además, tenía curiosidad por cronometrar dos implementaciones diferentes de maketrans para Python 3

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

vs

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

Además, agregué otro método para usar set, donde aprovecho la función de intersección para reducir el número de iteraciones.

Este es el 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))

Estos son mis resultados:

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


['string', 'With', 'Punctuation']
Pablo Rodriguez Bertorello
fuente
2
Edite con más información. Se desaconsejan las respuestas de solo código y "pruebe esto", ya que no contienen contenido que se pueda buscar y no explican por qué alguien debería "probar esto".
Paritosh
4

Aquí hay una solución sin expresiones regulares.

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
  • Reemplaza los signos de puntuación con espacios
  • Reemplace múltiples espacios entre palabras con un solo espacio
  • Elimine los espacios finales, si los hay con strip ()
ngub05
fuente
4

Una frase podría ser útil en casos no muy estrictos:

''.join([c for c in s if c.isalnum() or c.isspace()])
Dom Gray
fuente
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
fuente
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
fuente
0

¿Por qué ninguno de ustedes usa esto?

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

¿Demasiado lento?

Dehua Li
fuente
Tenga en cuenta que esto también eliminará espacios.
Georgy
0

Considerando unicode. Código verificado en 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
fuente
-1

Eliminar palabras de detención del archivo 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
fuente
-2

Me gusta usar una función 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
fuente
1
Esto es quitar caracteres del principio y del final; utilizar abc.strip(string.punctuation)en su lugar para eso. No eliminará tales caracteres en el medio .
Martijn Pieters