Najlepszy sposób na usunięcie interpunkcji z ciągu


636

Wydaje się, że powinien istnieć prostszy sposób niż:

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

Jest tu?


3
Wydaje mi się to całkiem proste. Dlaczego chcesz to zmienić? Jeśli chcesz łatwiej, po prostu zawiń to, co właśnie napisałeś w funkcji.
Hannes Ovrén

2
Cóż, po prostu wydawało się trochę hackingiem, aby używać pewnego rodzaju efektu ubocznego str.translate do wykonywania pracy. Myślałem, że może istnieć coś bardziej jak str. Pasek (znaki), który działał na całym łańcuchu, a nie tylko granice, które ominąłem.
Lawrence Johnston,

2
Zależy również od danych. Używanie tego w danych, w których występują nazwy serwerów z podkreśleniami jako częścią nazwy (dość często w niektórych miejscach) może być złe. Tylko upewnij się, że znasz dane i to, co one zawierają, albo możesz skończyć z podzbiorem problemu clbuttic.
EBGreen,

54
Zależy również od tego, co nazywasz interpunkcją. „ The temperature in the O'Reilly & Arbuthnot-Smythe server's main rack is 40.5 degrees.” zawiera dokładnie JEDEN znak interpunkcyjny, drugi „”.
John Machin

37
Dziwię się, że string.punctuationnikt nie wspomniał, że w ogóle nie zawiera interpunkcji w języku innym niż angielski. Myślę o。 ,!? : × „” 〟i tak dalej.
Clément

Odpowiedzi:


928

Z punktu widzenia wydajności nie pokonasz

s.translate(None, string.punctuation)

W przypadku wyższych wersji Pythona użyj następującego kodu:

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

Wykonuje operacje na surowym łańcuchu znaków w C z tabelą odnośników - niewiele to przebije, ale napisanie własnego kodu C.

Jeśli prędkość nie stanowi problemu, inną opcją jest:

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

Jest to szybsze niż s.replace z każdym char, ale nie działa tak dobrze, jak nie-czyste podejście do pythona, takie jak wyrażenia regularne lub string.translate, jak widać z poniższych czasów. W przypadku tego typu problemu robienie tego na jak najniższym poziomie się opłaca.

Kod czasowy:

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)

Daje to następujące wyniki:

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

27
Dzięki za informacje o czasie, myślałem o zrobieniu czegoś takiego sam, ale twój jest lepiej napisany niż cokolwiek, co bym zrobił, a teraz mogę go użyć jako szablonu dla dowolnego przyszłego kodu czasu, który chcę napisać :).
Lawrence Johnston,

29
Świetna odpowiedź. Możesz to uprościć, usuwając tabelę. Dokumenty mówią: „ustaw argument tabeli na Brak dla tłumaczeń, które usuwają tylko znaki” ( docs.python.org/library/stdtypes.html#str.translate )
Alexandros Marinos

3
Warto również zauważyć, że translate () zachowuje się inaczej dla obiektów str i Unicode, więc musisz mieć pewność, że zawsze pracujesz z tym samym typem danych, ale podejście w tej odpowiedzi działa równie dobrze dla obu, co jest przydatne.
Richard J

36
W Python3 table = string.maketrans("","")należy zastąpić table = str.maketrans({key: None for key in string.punctuation})?
SparkAndShine

19
Aktualizacja dyskusji od wersji Python 3.6 regexjest teraz najbardziej wydajną metodą! To prawie 2x szybsze niż tłumaczenie. Także zestawy i zamień nie są już takie złe! Oba zostały poprawione ponad 4
krotnie

143

Wyrażenia regularne są dość proste, jeśli je znasz.

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

4
@Outlier Objaśnienie: nie zastępuje znaków (^) ani spacji pustym łańcuchem. Uważaj jednak, \ \ dopasowania zbyt często podkreślają na przykład.
Matthias

4
@SIslam Myślę, że będzie działać z Unicode z ustawioną flagą Unicode, tj s = re.sub(r'[^\w\s]','',s, re.UNICODE). Testowanie go w Pythonie 3 na Linuksie działa nawet bez flagi przy użyciu liter tamilskich, தமிழ்.
Matthias

@Matthias Wypróbowałem kod z Pythonem 3.6.5 na Macu, wyjście tamilskich liter wygląda trochę inaczej, wejście தமிழ் zmienia się na தமழ. Nie mam wiedzy o tamilskim, nie jestem pewien, czy jest to oczekiwane.
shiouming

70

Dla wygody użytkowania podsumowuję notatkę usuwania interpunkcji z łańcucha zarówno w Pythonie 2, jak i Pythonie 3. Szczegółowy opis znajduje się w innych odpowiedziach.


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, próbowałem tego, ale to nie działa we wszystkich przypadkach. myString.translate (string.maketrans („”, „”), string.punctuation) działa dobrze.
Aidan Kane,

12
Zauważ, że strw Pythonie 3 i unicodePythonie 2 deletecharsargument nie jest obsługiwany.
agf

4
myString.translate (string.maketrans ( "", ""), string.punctuation) nie będzie działać z ciągów Unicode (dowiedziałem się na własnej skórze)
Marc Maxmeister

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

3
@BrianTingle: spójrz na kod Python 3 w moim komentarzu (przekazuje jeden argument). Kliknij ten link, aby zobaczyć kod Python 2, który działa z Unicode i jego adaptacją do Python 3
jfs

29

Zwykle używam czegoś takiego:

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

2
Uglified jedno-liner: reduce(lambda s,c: s.replace(c, ''), string.punctuation, s).
jfs

1
świetnie, jednak nie usuwa niektórych wahań, takich jak dłuższy łącznik
Vladimir Stazhilov,

25

string.punctuationjest tylko ASCII ! Bardziej poprawnym (ale także znacznie wolniejszym) sposobem jest użycie modułu 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

Możesz uogólniać i usuwać także inne typy znaków:

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

Usunie także znaki, ~*+§$które mogą, ale nie muszą być „znakami interpunkcyjnymi” w zależności od punktu widzenia.



Niestety, rzeczy takie jak ~nie należą do kategorii znaków interpunkcyjnych. Musisz także przetestować kategorię Symbole.
CJ Jackson,

24

Niekoniecznie prostsze, ale inny sposób, jeśli jesteś bardziej zaznajomiony z rodziną ponownie.

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

1
Działa, ponieważ string.punctuation ma sekwencję, -. w odpowiedniej, rosnącej, bez luk, w kolejności ASCII. Podczas gdy Python ma to prawo, kiedy próbujesz użyć podzbioru string.punctuation, może on być przerywnikiem pokazu z powodu zaskoczenia „-”.
S.Lott,

2
Właściwie to wciąż źle. Sekwencja „\]” jest traktowana jako ucieczka (przypadkowo nie zamyka], więc omija kolejny błąd), ale pozostawia \ nieskalowaną. Powinieneś użyć re.escape (string.punctuation), aby temu zapobiec.
Brian

1
Tak, pominąłem to, ponieważ działało to na przykład w celu uproszczenia, ale masz rację, że należy to uwzględnić.
Vinko Vrsalovic,

13

W przypadku wartości w języku Python 3 strlub Python 2 unicodepobierany jest str.translate()tylko słownik; punkty kodowe (liczby całkowite) są wyszukiwane w tym odwzorowaniu i wszystko, na co mapowane Nonejest usuwane.

Aby usunąć (trochę?) Interpunkcję, użyj:

import string

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

Metoda dict.fromkeys()klasy sprawia, że ​​tworzenie mapowania jest banalne, a wszystkie wartości są ustawiane na Nonepodstawie sekwencji kluczy.

Aby usunąć wszystkie znaki interpunkcyjne, a nie tylko znaki interpunkcyjne ASCII, twoja tabela musi być nieco większa; patrz odpowiedź JF Sebastiana (wersja 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'))

Do obsługi Unicode string.punctuationnie wystarczy. Zobacz moją odpowiedź
jfs

@JFSebastian: w rzeczywistości moja odpowiedź polegała na użyciu tych samych znaków, co ten, który otrzymał najwięcej głosów. Dodano wersję tabeli w języku Python 3.
Martijn Pieters

najlepiej głosowana odpowiedź działa tylko na łańcuchy ascii. Twoja odpowiedź twierdzi wprost, że obsługuje Unicode.
jfs

1
@JFSebastian: działa na ciągi znaków Unicode. Usuwa interpunkcję ASCII. Nigdy nie twierdziłem, że usuwa wszystkie interpunkcje. :-) Chodziło o to, aby zapewnić poprawną technikę dla unicodeobiektów w porównaniu do obiektów Python 2 str.
Martijn Pieters

12

string.punctuationbrakuje wielu znaków interpunkcyjnych, które są powszechnie używane w prawdziwym świecie. Co powiesz na rozwiązanie, które działa w przypadku interpunkcji spoza 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()

Osobiście uważam, że jest to najlepszy sposób na usunięcie interpunkcji z ciągu znaków w Pythonie, ponieważ:

  • Usuwa wszystkie znaki interpunkcyjne Unicode
  • Jest łatwo modyfikowalny, np. Możesz usunąć, \{S}jeśli chcesz usunąć interpunkcję, ale zachowaj symbole jak $.
  • Możesz uzyskać bardzo szczegółowe informacje na temat tego, co chcesz zachować i co chcesz usunąć, na przykład \{Pd}usuniesz tylko myślniki.
  • Ten wyrażenie regularne normalizuje również spacje. Mapuje tabulatory, znaki powrotu karetki i inne osobliwości na ładne, pojedyncze spacje.

Wykorzystuje to właściwości znaków Unicode, o których więcej można przeczytać na Wikipedii .


9

Nie widziałem jeszcze tej odpowiedzi. Wystarczy użyć wyrażenia regularnego; usuwa wszystkie znaki oprócz znaków słownych ( \w) i cyfr ( \d), a następnie białych znaków ( \s):

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

1
\djest zbędny, ponieważ jest podzbiorem \w.
blhsing 10.01.19

Znaki liczbowe są uważane za podzbiór znaków Word? Myślałem, że znak Word to dowolny znak, który mógłby skonstruować prawdziwe słowo, np. A-zA-Z?
Blairg23,

Tak, „słowo” w wyrażeniu regularnym obejmuje alfabety, cyfry i znak podkreślenia. Zobacz opis \ww dokumentacji: docs.python.org/3/library/re.html
blhsing 10.01.19

8

Oto jedna linijka dla 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

To może nie być najlepsze rozwiązanie, ale tak właśnie to zrobiłem.

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

6

Oto funkcja, którą napisałem. To nie jest bardzo wydajne, ale jest proste i możesz dodać lub usunąć dowolną interpunkcję:

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)

Wygląda na to, że działałoby to tylko dla znaków ASCII.
avirr

5

W ramach aktualizacji przepisałem przykład @Brian w Pythonie 3 i wprowadziłem w nim zmiany, aby przenieść krok kompilacji wyrażenia regularnego wewnątrz funkcji. Myślałem tutaj o tym, aby za każdym razem wykonać każdy krok potrzebny do działania tej funkcji. Być może używasz przetwarzania rozproszonego i nie możesz udostępnić obiektu wyrażenia regularnego między swoimi pracownikami i musisz mieć re.compilekrok na każdym z nich. Byłem też ciekawy, kiedy zaplanowałem dwie różne implementacje maketrans dla Python 3

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

vs

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

Dodatkowo dodałem kolejną metodę użycia zestawu, w której korzystam z funkcji przecięcia, aby zmniejszyć liczbę iteracji.

To jest kompletny kod:

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

To są moje wyniki:

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
Edytuj, podając więcej informacji. Odradzane są tylko kody i odpowiedzi „wypróbuj to”, ponieważ nie zawierają one treści, które można przeszukiwać, i nie wyjaśniają, dlaczego ktoś powinien „wypróbować to”.
Paritosh,

4

Oto rozwiązanie bez wyrażenia regularnego.

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
  • Zastępuje interpunkcje spacjami
  • Zamień wiele spacji między słowami na jedną spację
  • Usuń końcowe spacje, jeśli występują, za pomocą strip ()

4

Jednowarstwowy może być pomocny w niezbyt surowych przypadkach:

''.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

Dlaczego nikt z was tego nie używa?

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

Za wolno?


Pamiętaj, że spowoduje to również usunięcie spacji.
Georgy,

0

Biorąc pod uwagę Unicode. Kod sprawdzany w 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

Usuń słowa stop z pliku tekstowego za pomocą Pythona

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

Lubię używać takiej funkcji:

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
To usuwa znaki od początku i na końcu; użyj abc.strip(string.punctuation)zamiast tego do tego. Nie usunie takich postaci na środku .
Martijn Pieters
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.