Meilleure façon de supprimer la ponctuation d'une chaîne

639

Il semble qu'il devrait y avoir un moyen plus simple que:

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

Y a-t-il?

Lawrence Johnston
la source
3
Cela me semble assez simple. Pourquoi voulez-vous le changer? Si vous voulez que ce soit plus simple, enveloppez simplement ce que vous venez d'écrire dans une fonction.
Hannes Ovrén
2
Eh bien, il semblait juste un peu hackeux d'utiliser une sorte d'effet secondaire de str.translate pour faire le travail. Je pensais qu'il pourrait y avoir quelque chose de plus comme str.strip (chars) qui fonctionnait sur toute la chaîne au lieu des limites que j'avais manquées.
Lawrence Johnston
2
Cela dépend aussi des données. Utiliser cela sur des données où il y a des noms de serveur avec des soulignements dans le nom (assez communs à certains endroits) pourrait être mauvais. Assurez-vous simplement que vous connaissez les données et ce qu'elles contiennent ou vous pourriez vous retrouver avec un sous-ensemble du problème clbuttic.
EBGreen
54
Cela dépend aussi de ce que vous appelez la ponctuation. " The temperature in the O'Reilly & Arbuthnot-Smythe server's main rack is 40.5 degrees." contient exactement UN caractère de ponctuation, le second "."
John Machin
37
Je suis surpris que personne n'ait mentionné qu'il string.punctuationn'inclut pas du tout la ponctuation non anglaise. Je pense à。 ,!? : × “” 〟, etc.
Clément

Réponses:

930

Du point de vue de l'efficacité, vous n'allez pas battre

s.translate(None, string.punctuation)

Pour les versions supérieures de Python, utilisez le code suivant:

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

Il exécute des opérations de chaîne brutes en C avec une table de recherche - il n'y a pas grand-chose qui va battre cela, mais écrire votre propre code C.

Si la vitesse n'est pas un problème, une autre option est:

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

Ceci est plus rapide que le remplacement avec chaque caractère, mais ne fonctionnera pas aussi bien que les approches python non pures telles que les expressions régulières ou string.translate, comme vous pouvez le voir dans les timings ci-dessous. Pour ce type de problème, le faire au niveau le plus bas possible est payant.

Code temporel:

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)

Cela donne les résultats suivants:

sets      : 19.8566138744
regex     : 6.86155414581
translate : 2.12455511093
replace   : 28.4436721802
Brian
la source
27
Merci pour les informations de timing, je pensais à faire quelque chose comme ça moi-même, mais le vôtre est mieux écrit que tout ce que j'aurais fait et maintenant je peux l'utiliser comme modèle pour tout futur code de timing que je veux écrire :).
Lawrence Johnston
29
Très bonne réponse. Vous pouvez le simplifier en supprimant le tableau. Les documents disent: "définissez l'argument de table sur Aucun pour les traductions qui ne suppriment que les caractères" ( docs.python.org/library/stdtypes.html#str.translate )
Alexandros Marinos
3
il convient également de noter que translate () se comporte différemment pour les objets str et unicode, vous devez donc être sûr de toujours travailler avec le même type de données, mais l'approche de cette réponse fonctionne aussi bien pour les deux, ce qui est pratique.
Richard J
36
En Python3, table = string.maketrans("","")devrait être remplacé par table = str.maketrans({key: None for key in string.punctuation})?
SparkAndShine
19
Mettre à jour la discussion, à partir de Python 3.6, regexest maintenant la méthode la plus efficace! Il est presque 2 fois plus rapide que la traduction. De plus, les ensembles et les remplacements ne sont plus si mauvais! Ils sont tous les deux améliorés d'un facteur 4 :)
Ryan Soklaski
143

Les expressions régulières sont assez simples, si vous les connaissez.

import re
s = "string. With. Punctuation?"
s = re.sub(r'[^\w\s]','',s)
Ératosthène
la source
4
@Outlier Explication: remplace pas les (^) caractères ou espaces de mot par la chaîne vide. Attention cependant, le \ w correspond trop souvent au trait de soulignement par exemple.
Matthias
4
@SIslam Je pense que cela fonctionnera avec unicode avec l'ensemble d'indicateurs unicode, c'est-à-dire s = re.sub(r'[^\w\s]','',s, re.UNICODE). Le tester avec python 3 sur linux, il fonctionne même sans le drapeau en utilisant des lettres tamoules, தமிழ்.
Matthias
@Matthias J'ai essayé le code avec Python 3.6.5 sur Mac, la sortie des lettres tamoules est un peu différente, l'entrée தமிழ் devient தமழ. Je n'ai aucune connaissance du tamoul, je ne sais pas si c'est prévu.
shiouming
71

Pour la commodité de l'utilisation, je résume la note de la ponctuation de striping d'une chaîne en Python 2 et Python 3. Veuillez vous référer à d'autres réponses pour la description détaillée.


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
la source
51
myString.translate(None, string.punctuation)
pyrou
la source
4
ah, j'ai essayé mais ça ne marche pas dans tous les cas. myString.translate (string.maketrans ("", ""), string.punctuation) fonctionne très bien.
Aidan Kane du
12
Notez que pour strPython 3 et unicodePython 2, l' deletecharsargument n'est pas pris en charge.
agf
4
myString.translate (string.maketrans ("", ""), string.punctuation) ne fonctionnera PAS avec les chaînes unicode (découvert à la dure)
Marc Maxmeister
44
TypeError: translate() takes exactly one argument (2 given):(
Brian Tingle
3
@BrianTingle: regardez le code Python 3 dans mon commentaire (il passe un argument). Suivez le lien, pour voir le code Python 2 qui fonctionne avec unicode et son adaptation Python 3
jfs
29

J'utilise habituellement quelque chose comme ça:

>>> s = "string. With. Punctuation?" # Sample string
>>> import string
>>> for c in string.punctuation:
...     s= s.replace(c,"")
...
>>> s
'string With Punctuation'
S.Lott
la source
2
Un uglified one-liner: reduce(lambda s,c: s.replace(c, ''), string.punctuation, s).
jfs
1
génial, mais ne supprime pas une certaine puctuation comme un trait d'union plus long
Vladimir Stazhilov
25

string.punctuationest uniquement ASCII ! Une manière plus correcte (mais aussi beaucoup plus lente) consiste à utiliser le module 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

Vous pouvez également généraliser et supprimer d'autres types de caractères:

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

Il supprime également les caractères ~*+§$qui peuvent être ou non de la «ponctuation» selon le point de vue.

Björn Lindqvist
la source
3
Vous pourriez:regex.sub(ur"\p{P}+", "", text)
jfs
Malheureusement, des choses comme ~ne font pas partie de la catégorie de ponctuation. Vous devez également tester également la catégorie Symboles.
CJ Jackson
24

Pas nécessairement plus simple, mais d'une manière différente, si vous êtes plus familier avec la famille re.

import re, string
s = "string. With. Punctuation?" # Sample string 
out = re.sub('[%s]' % re.escape(string.punctuation), '', s)
Vinko Vrsalovic
la source
1
Fonctionne car string.punctuation a la séquence, -. dans un ordre ASCII approprié, croissant, sans espaces. Bien que Python ait ce droit, lorsque vous essayez d'utiliser un sous-ensemble de string.punctuation, il peut être un show-stopper à cause de la surprise "-".
S.Lott
2
En fait, c'est toujours faux. La séquence "\]" est traitée comme une fuite (par coïncidence ne fermant pas le] afin de contourner un autre échec), mais laisse \ inéchappé. Vous devez utiliser re.escape (string.punctuation) pour éviter cela.
Brian
1
Oui, je l'ai omis parce que cela a fonctionné pour l'exemple pour garder les choses simples, mais vous avez raison qu'il devrait être incorporé.
Vinko Vrsalovic
13

Pour les valeurs Python 3 strou Python 2 unicode, str.translate()prend uniquement un dictionnaire; les points de code (entiers) sont recherchés dans ce mappage et tout ce qui Noneest mappé est supprimé.

Pour supprimer (certains?) La ponctuation, utilisez:

import string

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

La dict.fromkeys()méthode de classe simplifie la création du mappage, en définissant toutes les valeurs en Nonefonction de la séquence de clés.

Pour supprimer toute ponctuation, pas seulement la ponctuation ASCII, votre table doit être un peu plus grande; voir la réponse de JF Sebastian (version 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
la source
Pour supporter Unicode, ce string.punctuationn'est pas suffisant. Voir ma réponse
jfs
@JFSebastian: en effet, ma réponse utilisait simplement les mêmes caractères que celui qui a été voté en premier. Ajout d'une version Python 3 de votre table.
Martijn Pieters
la réponse la plus votée ne fonctionne que pour les chaînes ascii. Votre réponse revendique explicitement le support Unicode.
jfs
1
@JFSebastian: cela fonctionne pour les chaînes Unicode. Il supprime la ponctuation ASCII. Je n'ai jamais prétendu que cela supprimait toute ponctuation. :-) Le but était de fournir la bonne technique pour les unicodeobjets vs les objets Python 2 str.
Martijn Pieters
12

string.punctuationmanque beaucoup de signes de ponctuation qui sont couramment utilisés dans le monde réel. Que diriez-vous d'une solution qui fonctionne pour la ponctuation non 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()

Personnellement, je pense que c'est la meilleure façon de supprimer la ponctuation d'une chaîne en Python car:

  • Il supprime toute la ponctuation Unicode
  • Il est facilement modifiable, par exemple, vous pouvez supprimer le \{S}si vous souhaitez supprimer la ponctuation, mais gardez les symboles comme $.
  • Vous pouvez être très précis sur ce que vous souhaitez conserver et ce que vous souhaitez supprimer, par exemple \{Pd}, ne supprimera que les tirets.
  • Cette expression régulière normalise également les espaces blancs. Il mappe les tabulations, les retours chariot et autres bizarreries à de beaux espaces uniques.

Cela utilise les propriétés des caractères Unicode, dont vous pouvez en savoir plus sur Wikipédia .

Zach
la source
9

Je n'ai pas encore vu cette réponse. Utilisez simplement une expression régulière; il supprime tous les caractères à l'exception des caractères de mot ( \w) et des caractères numériques ( \d), suivis d'un espace ( \s):

import re
s = "string. With. Punctuation?" # Sample string 
out = re.sub(ur'[^\w\d\s]+', '', s)
Blairg23
la source
1
\dest redondant car il s'agit d'un sous-ensemble de \w.
blhsing le
Les caractères numériques sont considérés comme un sous-ensemble de caractères Word? Je pensais qu'un caractère Word était n'importe quel caractère capable de construire un vrai mot, par exemple a-zA-Z?
Blairg23
Oui, un «mot» en expression régulière comprend des alphabets, des chiffres et des traits de soulignement. Veuillez consulter la description pour \wdans la documentation: docs.python.org/3/library/re.html
blhsing
8

Voici une ligne pour 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
la source
7

Ce n'est peut-être pas la meilleure solution, mais c'est ainsi que je l'ai fait.

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

Voici une fonction que j'ai écrite. Ce n'est pas très efficace, mais c'est simple et vous pouvez ajouter ou supprimer toute ponctuation que vous désirez:

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
la source
5
import re
s = "string. With. Punctuation?" # Sample string 
out = re.sub(r'[^a-zA-Z0-9\s]', '', s)
Haythem HADHAB
la source
Il semble que cela ne fonctionnerait que pour les caractères ASCII.
avril
5

Juste comme une mise à jour, j'ai réécrit l'exemple @Brian en Python 3 et y ai apporté des modifications pour déplacer l'étape de compilation regex à l'intérieur de la fonction. Ma pensée ici était de chronométrer chaque étape nécessaire pour faire fonctionner la fonction. Peut-être que vous utilisez l'informatique distribuée et que vous ne pouvez pas partager d'objet regex entre vos employés et que vous devez avoir une re.compileétape sur chaque employé. Aussi, j'étais curieux de chronométrer deux implémentations différentes de maketrans pour Python 3

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

contre

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

De plus, j'ai ajouté une autre méthode à utiliser set, où je profite de la fonction d'intersection pour réduire le nombre d'itérations.

Voici le code complet:

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

Voici mes résultats:

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


['string', 'With', 'Punctuation']
Pablo Rodriguez Bertorello
la source
2
Veuillez modifier avec plus d'informations. Les réponses de code uniquement et "essayez ceci" sont déconseillées, car elles ne contiennent aucun contenu consultable et n'expliquent pas pourquoi quelqu'un devrait "essayer ceci".
Paritosh
4

Voici une solution sans 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
  • Remplace les ponctuations par des espaces
  • Remplacez plusieurs espaces entre les mots par un seul espace
  • Supprimez les espaces de fin, le cas échéant avec strip ()
ngub05
la source
4

Un one-liner pourrait être utile dans des cas pas très stricts:

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

Pourquoi aucun de vous ne l'utilise?

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

Trop lent?

Dehua Li
la source
Notez que cela supprimera également les espaces.
Georgy
0

En considérant unicode. Code vérifié 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
la source
-1

Supprimer les mots vides du fichier texte à l'aide de 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
la source
-2

J'aime utiliser une fonction comme celle-ci:

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
Disk Giant
la source
1
Cela supprime les caractères du début et de la fin; utiliser à la abc.strip(string.punctuation)place pour cela. Il ne supprimera pas ces caractères au milieu .
Martijn Pieters