Il modo migliore per rimuovere la punteggiatura da una stringa


639

Sembra che ci dovrebbe essere un modo più semplice di:

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

È lì?


3
A me sembra piuttosto semplice. Perché vuoi cambiarlo? Se lo desideri, avvolgi semplicemente ciò che hai appena scritto in una funzione.
Hannes Ovrén,

2
Beh, mi è sembrato un po 'hacker usare una specie di effetto collaterale di str.translate per fare il lavoro. Stavo pensando che potrebbe esserci qualcosa di più simile a str.strip (chars) che ha funzionato sull'intera stringa anziché sui limiti che mi ero perso.
Lawrence Johnston,

2
Dipende anche dai dati. Usarlo su dati in cui ci sono nomi di server con caratteri di sottolineatura come parte del nome (luoghi piuttosto comuni) potrebbe essere male. Assicurati solo di conoscere i dati e cosa contengono o potresti finire con un sottoinsieme del problema clbuttic.
EBGreen

54
Dipende anche da ciò che chiami punteggiatura. " The temperature in the O'Reilly & Arbuthnot-Smythe server's main rack is 40.5 degrees." contiene esattamente UN carattere di punteggiatura, il secondo "."
John Machin

37
Sono sorpreso che nessuno abbia menzionato che string.punctuationnon include affatto punteggiatura non inglese. Sto pensando a。 ,!? : × “” 〟e così via.
Clément,

Risposte:


930

Dal punto di vista dell'efficienza, non batterai

s.translate(None, string.punctuation)

Per le versioni successive di Python utilizzare il seguente codice:

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

Sta eseguendo operazioni di stringa non elaborate in C con una tabella di ricerca - non c'è molto che lo batterà ma scrivendo il proprio codice C.

Se la velocità non è un problema, un'altra opzione è:

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

Questo è più veloce di s.replace con ogni carattere, ma non eseguirà approcci in pitone non puri come regex o string.translate, come puoi vedere dai tempi seguenti. Per questo tipo di problema, ripagarlo a un livello il più basso possibile.

Codice temporale:

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)

Questo dà i seguenti risultati:

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

27
Grazie per le informazioni sui tempi, stavo pensando di fare qualcosa del genere da solo, ma il tuo è meglio scritto di qualsiasi cosa avrei fatto e ora posso usarlo come modello per qualsiasi codice di tempo futuro che voglio scrivere :).
Lawrence Johnston,

29
Bella risposta. Puoi semplificarlo rimuovendo la tabella. I documenti dicono: "imposta l'argomento della tabella su Nessuno per le traduzioni che eliminano solo i caratteri" ( docs.python.org/library/stdtypes.html#str.translate )
Alexandros Marinos,

3
vale anche la pena notare che translate () si comporta in modo diverso per gli oggetti str e unicode, quindi devi essere sicuro di lavorare sempre con lo stesso tipo di dati, ma l'approccio in questa risposta funziona ugualmente bene per entrambi, il che è utile.
Richard J,

36
In Python3, table = string.maketrans("","")dovrebbe essere sostituito con table = str.maketrans({key: None for key in string.punctuation})?
SparkAndShine

19
Aggiornare la discussione, a partire da Python 3.6, regexè ora il metodo più efficiente! È quasi 2 volte più veloce di tradurre. Inoltre, i set e le sostituzioni non sono più così male! Entrambi sono migliorati di oltre un fattore 4 :)
Ryan Soklaski,

143

Le espressioni regolari sono abbastanza semplici, se le conosci.

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

4
@Outlier Spiegazione: sostituisce i caratteri o gli spazi non (^) della parola con la stringa vuota. Attenzione però, per esempio, la sottolineatura delle corrispondenze \ w troppo spesso.
Matthias,

4
@SIslam Penso che funzionerà con Unicode con il flag Unicode impostato, ad es s = re.sub(r'[^\w\s]','',s, re.UNICODE). Provandolo con Python 3 su Linux funziona anche senza la bandiera usando lettere tamil, தமிழ்.
Matthias,

@Matthias Ho provato il codice con Python 3.6.5 su Mac, l'output delle lettere tamil sembra un po 'diverso, l'input தமிழ் diventa தமழ. Non ho conoscenza del Tamil, non sono sicuro se ci si aspetta.
shiouming,

71

Per comodità di utilizzo, riassumo la nota di come rimuovere la punteggiatura da una stringa sia in Python 2 che in Python 3. Fare riferimento ad altre risposte per la descrizione dettagliata.


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

51
myString.translate(None, string.punctuation)

4
ah, l'ho provato ma non funziona in tutti i casi. myString.translate (string.maketrans ("", ""), string.punctuation) funziona bene.
Aidan Kane,

12
Si noti che per strin Python 3 e unicodein Python 2, l' deletecharsargomento non è supportato.
agf

4
myString.translate (string.maketrans ("", ""), string.punctuation) NON funzionerà con stringhe unicode (trovate nel modo più difficile)
Marc Maxmeister,

44
TypeError: translate() takes exactly one argument (2 given):(
Brian Tingle,

3
@BrianTingle: guarda il codice Python 3 nel mio commento (passa un argomento). Segui il link per vedere il codice Python 2 che funziona con Unicode e il suo adattamento Python 3
jfs

29

Di solito uso qualcosa del genere:

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

2
Un uglified one-liner: reduce(lambda s,c: s.replace(c, ''), string.punctuation, s).
jfs,

1
grande, tuttavia non rimuove alcune puttuazioni come il trattino più lungo
Vladimir Stazhilov,

25

string.punctuationè solo ASCII ! Un modo più corretto (ma anche molto più lento) è usare il modulo 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

Puoi generalizzare e rimuovere anche altri tipi di caratteri:

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

Spoglierà anche caratteri come quelli ~*+§$che possono o meno essere "punteggiatura" a seconda del proprio punto di vista.



Sfortunatamente, cose come ~non fanno parte della categoria di punteggiatura. Devi anche provare per la categoria Simboli.
CJ Jackson,

24

Non necessariamente più semplice, ma diverso, se hai più familiarità con la famiglia.

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

1
Funziona perché string.punctuation ha la sequenza, -. nell'ordine ASCII corretto, crescente, senza spazi vuoti. Mentre Python ha questo diritto, quando provi a usare un sottoinsieme di string.punctuation, può essere uno show-stopper a causa della sorpresa "-".
S. Lott,

2
In realtà, è ancora sbagliato. La sequenza "\]" viene trattata come una via di fuga (per coincidenza, non chiudendo il] in modo da eludere un altro errore), ma lascia \ senza caratteri di escape. Dovresti usare re.escape (string.punctuation) per evitarlo.
Brian,

1
Sì, l'ho omesso perché ha funzionato nell'esempio per semplificare le cose, ma hai ragione a dire che dovrebbe essere incorporato.
Vinko Vrsalovic,

13

Per i valori di Python 3 stro Python 2 unicode, str.translate()accetta solo un dizionario; i punti di codice (numeri interi) vengono cercati in quella mappatura e tutto ciò che viene mappato Noneviene rimosso.

Per rimuovere la punteggiatura (alcuni?), Quindi:

import string

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

Il dict.fromkeys()metodo class rende banale la creazione della mappatura, impostando tutti i valori in Nonebase alla sequenza di chiavi.

Per rimuovere tutta la punteggiatura, non solo la punteggiatura ASCII, la tabella deve essere leggermente più grande; vedi la risposta di JF Sebastian (versione 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'))

Per supportare Unicode, string.punctuationnon è abbastanza. Vedi la mia risposta
jfs

@JFSebastian: in effetti, la mia risposta è stata semplicemente quella di utilizzare gli stessi personaggi di quello più votato. Aggiunta una versione Python 3 della tabella.
Martijn Pieters

la risposta più votata funziona solo per le stringhe ASCII. La tua risposta richiede esplicitamente il supporto Unicode.
jfs,

1
@JFSebastian: funziona per le stringhe Unicode. Elimina la punteggiatura ASCII. Non ho mai sostenuto che mette a nudo tutto punteggiatura. :-) Il punto era fornire la tecnica corretta per gli unicodeoggetti rispetto agli oggetti Python 2 str.
Martijn Pieters

12

string.punctuationmanca un sacco di segni di punteggiatura che sono comunemente usati nel mondo reale. Che ne dici di una soluzione che funzioni per la punteggiatura 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()

Personalmente, credo che questo sia il modo migliore per rimuovere la punteggiatura da una stringa in Python perché:

  • Rimuove tutta la punteggiatura Unicode
  • È facilmente modificabile, ad esempio è possibile rimuovere il \{S}simbolo se si desidera rimuovere la punteggiatura, ma mantenere simboli simili $.
  • Puoi ottenere informazioni specifiche su ciò che desideri conservare e ciò che desideri rimuovere, ad esempio \{Pd}rimuoveranno solo i trattini.
  • Questa regex normalizza anche gli spazi bianchi. Associa schede, ritorni a capo e altre stranezze a spazi singoli e gradevoli.

Questo utilizza le proprietà dei caratteri Unicode, di cui puoi leggere di più su Wikipedia .


9

Non ho ancora visto questa risposta. Usa solo una regex; rimuove tutti i caratteri oltre ai caratteri di parola ( \w) e numerici ( \d), seguito da un carattere di spazio bianco ( \s):

import re
s = "string. With. Punctuation?" # Sample string 
out = re.sub(ur'[^\w\d\s]+', '', s)

1
\dè ridondante poiché è un sottoinsieme di \w.
blhsing il

I caratteri numerici sono considerati un sottoinsieme di caratteri Word? Pensavo che un personaggio di Word fosse un personaggio che potesse costruire una parola reale, ad esempio a-zA-Z?
Blairg23,

Sì, una "parola" in regex include alfabeti, numeri e trattino basso. Si prega di consultare la descrizione \wnella documentazione: docs.python.org/3/library/re.html
blhsing

8

Ecco un one-liner per 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}))

7

Questa potrebbe non essere la soluzione migliore, tuttavia è così che l'ho fatto.

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

6

Ecco una funzione che ho scritto. Non è molto efficiente, ma è semplice e puoi aggiungere o rimuovere la punteggiatura che desideri:

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

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

Sembra che funzionerebbe solo con caratteri ASCII.
avirr

5

Proprio come un aggiornamento, ho riscritto l'esempio @Brian in Python 3 e ho apportato modifiche per spostare il passo di compilazione regex all'interno della funzione. Il mio pensiero qui era quello di valutare ogni singolo passaggio necessario per far funzionare la funzione. Forse stai usando il calcolo distribuito e non puoi avere oggetti regex condivisi tra i tuoi lavoratori e devi avere un re.compilepasso per ogni lavoratore. Inoltre, ero curioso di valutare due diverse implementazioni di maketran per Python 3

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

vs

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

Inoltre ho aggiunto un altro metodo per utilizzare set, in cui utilizzo la funzione di intersezione per ridurre il numero di iterazioni.

Questo è il codice 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))

Questo è il mio risultato:

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

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


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

2
Modifica con ulteriori informazioni. Le risposte di solo codice e "prova questo" sono scoraggiate, perché non contengono contenuti ricercabili e non spiegano perché qualcuno dovrebbe "provare questo".
Paritosh,

4

Ecco una soluzione senza 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
  • Sostituisce le punteggiatura con spazi
  • Sostituisci più spazi tra le parole con un singolo spazio
  • Rimuovere gli spazi finali, se presenti con strip ()

4

Un one-liner potrebbe essere utile in casi non molto severi:

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

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

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 ("____________")

0

Perché nessuno di voi lo usa?

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

Troppo lento?


Nota che questo rimuoverà anche gli spazi.
Georgy,

0

Considerando unicode. Codice verificato in 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'))

-1

Rimuovi le parole di arresto dal file di testo 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')

-2

Mi piace usare una funzione come questa:

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

1
Questo è spogliare i personaggi dall'inizio e dalla fine; usare abc.strip(string.punctuation)invece per quello. Non rimuoverà tali personaggi nel mezzo .
Martijn Pieters
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.