Entfernen von allem außer alphanumerischen Zeichen aus einer Zeichenfolge in Python


336

Was ist der beste Weg, um mit Python alle nicht alphanumerischen Zeichen aus einer Zeichenfolge zu entfernen?

Die in der PHP-Variante dieser Frage vorgestellten Lösungen funktionieren wahrscheinlich mit einigen geringfügigen Anpassungen, scheinen mir jedoch nicht sehr "pythonisch" zu sein.

Für die Aufzeichnung möchte ich nicht nur Punkte und Kommas (und andere Satzzeichen) entfernen, sondern auch Anführungszeichen, Klammern usw.


7
Interessieren Sie sich für internationale alphanumerische Zeichen wie 'æøå', 'مرحبا', 'สวัสดี', 'こ ん に ち は'?
Pimin Konstantin Kefaloukos

4
@PiminKonstantinKefaloukos Ja, ich interessiere mich für die internationalen Zeichen, daher mein Kommentar zur akzeptierten Antwort für die Verwendung von re.UNICODE.
Mark van Lent

Antworten:


335

Ich habe gerade einige Funktionen aus Neugier zeitlich festgelegt. In diesen Tests entferne ich nicht alphanumerische Zeichen aus der Zeichenfolge string.printable(Teil des integrierten stringModuls). Die Verwendung von kompiliert '[\W_]+'und pattern.sub('', str)wurde als am schnellsten befunden.

$ python -m timeit -s \
     "import string" \
     "''.join(ch for ch in string.printable if ch.isalnum())" 
10000 loops, best of 3: 57.6 usec per loop

$ python -m timeit -s \
    "import string" \
    "filter(str.isalnum, string.printable)"                 
10000 loops, best of 3: 37.9 usec per loop

$ python -m timeit -s \
    "import re, string" \
    "re.sub('[\W_]', '', string.printable)"
10000 loops, best of 3: 27.5 usec per loop

$ python -m timeit -s \
    "import re, string" \
    "re.sub('[\W_]+', '', string.printable)"                
100000 loops, best of 3: 15 usec per loop

$ python -m timeit -s \
    "import re, string; pattern = re.compile('[\W_]+')" \
    "pattern.sub('', string.printable)" 
100000 loops, best of 3: 11.2 usec per loop

2
Sehr interessante Ergebnisse: Ich hätte erwartet, dass die regulären Ausdrücke langsamer sind. Interessanterweise habe ich dies mit einer anderen Option versucht ( valid_characters = string.ascii_letters + string.digitsgefolgt von join(ch for ch in string.printable if ch in valid_characters)und es war 6 Mikrosekunden schneller als die isalnum()Option. Immer noch viel langsamer als der reguläre Ausdruck.
DrAl

+1, Messzeit ist gut! (aber im vorletzten, pattern.sub('', string.printable)stattdessen - dumm, re.sub aufzurufen, wenn Sie ein RE-Objekt haben! -).
Alex Martelli

46
Für die Aufzeichnung: Verwenden re.compile('[\W_]+', re.UNICODE)Sie diese Option, um die Unicode-Sicherheit zu gewährleisten.
Mark van Lent

3
Wie machst du das, ohne den Leerraum zu entfernen?
Maudulus

6
Mach es, ohne den Leerraum zu entfernen: re.sub ('[\ W _] +', '', Satz, flags = re.UNICODE)
PALEN

266

Regelmäßige Ausdrücke zur Rettung:

import re
re.sub(r'\W+', '', your_string)

Nach Python-Definition '\W== [^a-zA-Z0-9_], die alle ausschließt numbers, lettersund_


2
Was macht das Pluszeichen im regulären Ausdruck? (Ich weiß, was es bedeutet, nur neugierig, warum es für die Wiederholung benötigt wird.)
Mark van Lent

7
@Mark: Ich kann mir vorstellen, dass dies die Ersetzung beschleunigen würde, da durch das Ersetzen alle Nicht-Wort-Zeichen in einem Block auf einmal entfernt werden, anstatt sie einzeln zu entfernen.
DrAl

2
Ja, das habe ich vor einiger Zeit beim Optimieren von leistungskritischem Code überprüft. Wenn es signifikante Spannweiten von Zeichen gibt, die ersetzt werden sollen, ist die Beschleunigung enorm.
Ants Aasma

20
Dies ist in diesem Fall möglicherweise nicht relevant, \Wbehält aber auch die Unterstriche bei.
Blixt

12
Wenn Sie nach dem Tipp von @Blixt nur Buchstaben und Zahlen möchten, können Sie dies erneut tun (r '[^ a-zA-Z0-9]', '', your_string)
Nigini

68

Verwenden Sie die Methode str.translate () .

Vorausgesetzt, Sie tun dies häufig:

(1) Erstellen Sie einmal eine Zeichenfolge mit allen Zeichen, die Sie löschen möchten:

delchars = ''.join(c for c in map(chr, range(256)) if not c.isalnum())

(2) Wann immer Sie eine Zeichenfolge zerquetschen möchten:

scrunched = s.translate(None, delchars)

Die Einrichtungskosten sind wahrscheinlich im Vergleich zu re.compile günstig. Die Grenzkosten sind viel niedriger:

C:\junk>\python26\python -mtimeit -s"import string;d=''.join(c for c in map(chr,range(256)) if not c.isalnum());s=string.printable" "s.translate(None,d)"
100000 loops, best of 3: 2.04 usec per loop

C:\junk>\python26\python -mtimeit -s"import re,string;s=string.printable;r=re.compile(r'[\W_]+')" "r.sub('',s)"
100000 loops, best of 3: 7.34 usec per loop

Hinweis: Die Verwendung von string.printable als Benchmark-Daten verschafft dem Muster '[\ W _] +' einen unfairen Vorteil . Alle nicht alphanumerischen Zeichen befinden sich in einem Bündel. In typischen Daten wäre mehr als eine Ersetzung erforderlich:

C:\junk>\python26\python -c "import string; s = string.printable; print len(s),repr(s)"
100 '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'

Folgendes passiert, wenn Sie re.sub etwas mehr Arbeit geben:

C:\junk>\python26\python -mtimeit -s"d=''.join(c for c in map(chr,range(256)) if not c.isalnum());s='foo-'*25" "s.translate(None,d)"
1000000 loops, best of 3: 1.97 usec per loop

C:\junk>\python26\python -mtimeit -s"import re;s='foo-'*25;r=re.compile(r'[\W_]+')" "r.sub('',s)"
10000 loops, best of 3: 26.4 usec per loop

1
Die Verwendung von translate ist in der Tat viel schneller. Selbst wenn unmittelbar vor dem Ersetzen / Übersetzen eine for-Schleife hinzugefügt wird (damit die Einrichtungskosten weniger wiegen), ist die Übersetzung ungefähr 17-mal schneller als der reguläre Ausdruck auf meinem Computer. Gut zu wissen.
Mark van Lent

3
Dies ist definitiv die pythonischste Lösung.
Codygman

1
Das hat mich fast überzeugt, aber ich würde vorschlagen, string.punctuationanstelle von''.join(c for c in map(chr, range(256)) if not c.isalnum())
ArnauOrriols

1
Beachten Sie, dass dies für strObjekte funktioniert , jedoch nicht für unicodeObjekte.
Yavar

@ John Machin Ist das im Wesentlichen ein Listenverständnis, an das als Argument weitergegeben wird .join()?
AdjunctProfessorFalcon

41

Du könntest es versuchen:

print ''.join(ch for ch in some_string if ch.isalnum())

15
>>> import re
>>> string = "Kl13@£$%[};'\""
>>> pattern = re.compile('\W')
>>> string = re.sub(pattern, '', string)
>>> print string
Kl13

Ich habe Ihre Antwort geliebt, aber sie entfernt auch die arabischen Zeichen. Können Sie mir sagen, wie ich sie behalten soll
?

13

Wie wäre es mit:

def ExtractAlphanumeric(InputString):
    from string import ascii_letters, digits
    return "".join([ch for ch in InputString if ch in (ascii_letters + digits)])

Dies funktioniert mithilfe des Listenverständnisses, um eine Liste der Zeichen zu erstellen, in denen InputStringsie in der Kombination ascii_lettersund den digitsZeichenfolgen vorhanden sind. Anschließend wird die Liste zu einer Zeichenfolge zusammengefügt.


Es scheint, dass string.ascii_letters nur Buchstaben (duh) und keine Zahlen enthält. Ich brauche auch die Zahlen ...
Mark van Lent

Das Hinzufügen von string.digits würde in der Tat das gerade erwähnte Problem lösen. :)
Mark van Lent

Ja, das wurde mir klar, als ich zurückkam, um Ihre Frage zu lesen. Hinweis für sich selbst: Lesen lernen!
DrAl

4

Als Nebeneffekt einiger anderer Antworten biete ich eine wirklich einfache und flexible Möglichkeit, eine Reihe von Zeichen zu definieren, auf die Sie den Inhalt einer Zeichenfolge beschränken möchten. In diesem Fall erlaube ich alphanumerische Zeichen und Unterstriche. Fügen Sie einfach Zeichen zu meinem hinzu oder entfernen PERMITTED_CHARSSie sie, je nach Anwendungsfall.

PERMITTED_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-" 
someString = "".join(c for c in someString if c in PERMITTED_CHARS)

2
Anstatt die zulässigen Zeichen, die für subtile Fehler anfällig sind, fest zu codieren, verwenden Sie string.digits + string.ascii_letters + '_-'.
Reti43

Ihr Vorschlag ist nicht falsch, aber er speichert auch nicht viele Zeichen des "Tippens", wenn dies Ihr Ziel ist. Wenn Sie meinen Beitrag kopieren, haben Sie auch keinen Tippfehler! Der eigentliche Punkt meiner Antwort ist jedoch, ein explizites, offenes und einfaches Mittel zuzulassen, um genau zu definieren, welche Zeichen Sie zulassen möchten.
BuvinJ

Als Mittelweg können Sie diese Vorschläge kombinieren SPECIAL_CHARS = '_-'und dann verwendenstring.digits + string.ascii_letters + SPECIAL_CHARS
BuvinJ

Es war ein Vorschlag in Bezug auf das, was vernünftig ist, es sei denn, wir machen Code-Golf. Das "Gehen" über die Tastatur, um 52 Buchstaben einzugeben, dauert erheblich länger als das Importieren eines Pakets, um ein oder zwei Objekte zu verwenden. Und das beinhaltet nicht die Zeit, um zu überprüfen, ob Sie alles richtig eingegeben haben. Es geht um gute Praktiken, das ist alles.
Reti43

Ich höre dich! Mein eigentlicher Punkt hier ist extreme Flexibilität, falls Sie mit Ihrem Zeichensatz genauer werden möchten.
BuvinJ

4
sent = "".join(e for e in sent if e.isalpha())

Ich werde versuchen zu erklären: Es geht durch alle Zeichenfolgen in e for e in sentund prüft via- if e.isalpha()Anweisung, ob das aktuelle Zeichen ein alphabetisches Symbol ist. Wenn ja, verbindet es mit der sentVariablen via sent = "".join()und alle nicht-alphabetischen Symbole werden durch ""(leere Zeichenfolge) ersetzt, weil der joinFunktion.
Sysanin

Ist dies nicht extrem langsam, da dies eine Schleife pro Zeichen macht, anstatt sich auf C-Regex zu verlassen?
dcsan

3
for char in my_string:
    if not char.isalnum():
        my_string = my_string.replace(char,"")

2

Timing mit zufälligen Zeichenfolgen von ASCII-Ausdrucken:

from inspect import getsource
from random import sample
import re
from string import printable
from timeit import timeit

pattern_single = re.compile(r'[\W]')
pattern_repeat = re.compile(r'[\W]+')
translation_tb = str.maketrans('', '', ''.join(c for c in map(chr, range(256)) if not c.isalnum()))


def generate_test_string(length):
    return ''.join(sample(printable, length))


def main():
    for i in range(0, 60, 10):
        for test in [
            lambda: ''.join(c for c in generate_test_string(i) if c.isalnum()),
            lambda: ''.join(filter(str.isalnum, generate_test_string(i))),
            lambda: re.sub(r'[\W]', '', generate_test_string(i)),
            lambda: re.sub(r'[\W]+', '', generate_test_string(i)),
            lambda: pattern_single.sub('', generate_test_string(i)),
            lambda: pattern_repeat.sub('', generate_test_string(i)),
            lambda: generate_test_string(i).translate(translation_tb),

        ]:
            print(timeit(test), i, getsource(test).lstrip('            lambda: ').rstrip(',\n'), sep='\t')


if __name__ == '__main__':
    main()

Ergebnis (Python 3.7):

       Time       Length                           Code                           
6.3716264850008880  00  ''.join(c for c in generate_test_string(i) if c.isalnum())
5.7285426190064750  00  ''.join(filter(str.isalnum, generate_test_string(i)))
8.1875841680011940  00  re.sub(r'[\W]', '', generate_test_string(i))
8.0002205439959650  00  re.sub(r'[\W]+', '', generate_test_string(i))
5.5290945199958510  00  pattern_single.sub('', generate_test_string(i))
5.4417179649972240  00  pattern_repeat.sub('', generate_test_string(i))
4.6772285089973590  00  generate_test_string(i).translate(translation_tb)
23.574712151996210  10  ''.join(c for c in generate_test_string(i) if c.isalnum())
22.829975890002970  10  ''.join(filter(str.isalnum, generate_test_string(i)))
27.210196289997840  10  re.sub(r'[\W]', '', generate_test_string(i))
27.203713296003116  10  re.sub(r'[\W]+', '', generate_test_string(i))
24.008979928999906  10  pattern_single.sub('', generate_test_string(i))
23.945240008994006  10  pattern_repeat.sub('', generate_test_string(i))
21.830899796994345  10  generate_test_string(i).translate(translation_tb)
38.731336012999236  20  ''.join(c for c in generate_test_string(i) if c.isalnum())
37.942474347000825  20  ''.join(filter(str.isalnum, generate_test_string(i)))
42.169366310001350  20  re.sub(r'[\W]', '', generate_test_string(i))
41.933375883003464  20  re.sub(r'[\W]+', '', generate_test_string(i))
38.899814646996674  20  pattern_single.sub('', generate_test_string(i))
38.636144253003295  20  pattern_repeat.sub('', generate_test_string(i))
36.201238164998360  20  generate_test_string(i).translate(translation_tb)
49.377356811004574  30  ''.join(c for c in generate_test_string(i) if c.isalnum())
48.408927293996385  30  ''.join(filter(str.isalnum, generate_test_string(i)))
53.901889764994850  30  re.sub(r'[\W]', '', generate_test_string(i))
52.130339455994545  30  re.sub(r'[\W]+', '', generate_test_string(i))
50.061149017004940  30  pattern_single.sub('', generate_test_string(i))
49.366573111998150  30  pattern_repeat.sub('', generate_test_string(i))
46.649754120997386  30  generate_test_string(i).translate(translation_tb)
63.107938601999194  40  ''.join(c for c in generate_test_string(i) if c.isalnum())
65.116287978999030  40  ''.join(filter(str.isalnum, generate_test_string(i)))
71.477421126997800  40  re.sub(r'[\W]', '', generate_test_string(i))
66.027950693998720  40  re.sub(r'[\W]+', '', generate_test_string(i))
63.315361931003280  40  pattern_single.sub('', generate_test_string(i))
62.342320287003530  40  pattern_repeat.sub('', generate_test_string(i))
58.249303059004890  40  generate_test_string(i).translate(translation_tb)
73.810345625002810  50  ''.join(c for c in generate_test_string(i) if c.isalnum())
72.593953348005020  50  ''.join(filter(str.isalnum, generate_test_string(i)))
76.048324580995540  50  re.sub(r'[\W]', '', generate_test_string(i))
75.106637657001560  50  re.sub(r'[\W]+', '', generate_test_string(i))
74.681338128997600  50  pattern_single.sub('', generate_test_string(i))
72.430461594005460  50  pattern_repeat.sub('', generate_test_string(i))
69.394243567003290  50  generate_test_string(i).translate(translation_tb)

str.maketrans& str.translateist am schnellsten, enthält jedoch alle Nicht-ASCII-Zeichen. re.compile& pattern.subist langsamer, aber irgendwie schneller als ''.join& filter.


-1

Wenn ich richtig verstanden habe, ist es am einfachsten, reguläre Ausdrücke zu verwenden, da dies Ihnen viel Flexibilität bietet. Die andere einfache Methode, die für die Schleifenverfolgung verwendet wird, ist der Code mit Beispiel. Ich habe auch das Auftreten von Wörtern gezählt und im Wörterbuch gespeichert.

s = """An... essay is, generally, a piece of writing that gives the author's own 
argument — but the definition is vague, 
overlapping with those of a paper, an article, a pamphlet, and a short story. Essays 
have traditionally been 
sub-classified as formal and informal. Formal essays are characterized by "serious 
purpose, dignity, logical 
organization, length," whereas the informal essay is characterized by "the personal 
element (self-revelation, 
individual tastes and experiences, confidential manner), humor, graceful style, 
rambling structure, unconventionality 
or novelty of theme," etc.[1]"""

d = {}      # creating empty dic      
words = s.split() # spliting string and stroing in list
for word in words:
    new_word = ''
    for c in word:
        if c.isalnum(): # checking if indiviual chr is alphanumeric or not
            new_word = new_word + c
    print(new_word, end=' ')
    # if new_word not in d:
    #     d[new_word] = 1
    # else:
    #     d[new_word] = d[new_word] +1
print(d)

Bitte bewerten Sie dies, wenn diese Antwort nützlich ist!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.