Zeichen außer Ziffern mit Python aus der Zeichenfolge entfernen?

137

Wie kann ich alle Zeichen außer Zahlen aus der Zeichenfolge entfernen?

Jan Tojnar
quelle
@ Jan Tojnar: Können Sie ein Beispiel geben?
João Silva
@JG: Ich habe gtk.Entry () und ich möchte multiplizieren float eingegeben.
Jan Tojnar
1
@JanTojnar verwendet die re.sub-Methode gemäß Antwort zwei und listet explizit auf, welche Zeichen zu behalten sind, z. B. re.sub ("[^ 0123456789 \.]", "", "Poo123.4and5fish")
Roger Heathcote

Antworten:

112

In Python 2. * ist der mit Abstand schnellste Ansatz die .translateMethode:

>>> x='aaa12333bb445bb54b5b52'
>>> import string
>>> all=string.maketrans('','')
>>> nodigs=all.translate(all, string.digits)
>>> x.translate(all, nodigs)
'1233344554552'
>>> 

string.maketranserstellt eine Übersetzungstabelle (eine Zeichenfolge mit der Länge 256), die in diesem Fall dieselbe ist wie ''.join(chr(x) for x in range(256))(nur schneller zu erstellen ;-). .translatewendet die Übersetzungstabelle an (die hier irrelevant ist, da sie im allWesentlichen Identität bedeutet) UND löscht Zeichen, die im zweiten Argument vorhanden sind - dem Schlüsselteil.

.translatefunktioniert sehr unterschiedlich auf Unicode - Strings (und Strings in Python 3 - ich tue Wunsch Fragen festgelegt , welche Hauptrelease von Python von Interesse ist!) - das nicht ganz einfach, das nicht ganz schnell, aber immer noch recht brauchbar.

Zurück zu 2. * ist der Leistungsunterschied beeindruckend ...:

$ python -mtimeit -s'import string; all=string.maketrans("", ""); nodig=all.translate(all, string.digits); x="aaa12333bb445bb54b5b52"' 'x.translate(all, nodig)'
1000000 loops, best of 3: 1.04 usec per loop
$ python -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'
100000 loops, best of 3: 7.9 usec per loop

Das 7-8-fache zu beschleunigen ist kaum Erdnuss, daher ist die translateMethode es wert, sie zu kennen und anzuwenden. Der andere beliebte Nicht-RE-Ansatz ...:

$ python -mtimeit -s'x="aaa12333bb445bb54b5b52"' '"".join(i for i in x if i.isdigit())'
100000 loops, best of 3: 11.5 usec per loop

ist 50% langsamer als RE, daher .translateschlägt der Ansatz ihn um mehr als eine Größenordnung.

In Python 3 oder für Unicode müssen Sie .translateeine Zuordnung (mit Ordnungszahlen, nicht Zeichen direkt als Schlüssel) übergeben, die Nonefür das zurückgibt, was Sie löschen möchten. Hier ist eine bequeme Möglichkeit, dies auszudrücken, um "alles außer" ein paar Zeichen zu löschen:

import string

class Del:
  def __init__(self, keep=string.digits):
    self.comp = dict((ord(c),c) for c in keep)
  def __getitem__(self, k):
    return self.comp.get(k)

DD = Del()

x='aaa12333bb445bb54b5b52'
x.translate(DD)

emittiert auch '1233344554552'. Wenn wir dies jedoch in xx.py einfügen, haben wir ...:

$ python3.1 -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'
100000 loops, best of 3: 8.43 usec per loop
$ python3.1 -mtimeit -s'import xx; x="aaa12333bb445bb54b5b52"' 'x.translate(xx.DD)'
10000 loops, best of 3: 24.3 usec per loop

... was zeigt, dass der Leistungsvorteil für diese Art von "Lösch" -Aufgaben verschwindet und zu einem Leistungsabfall wird.

Alex Martelli
quelle
1
@sunqiang, ja, absolut - es gibt einen Grund, warum Py3k anstelle von Byte-Strings wie in Py2 zu Unicode als THE-Text-String-Typ gewechselt ist - der gleiche Grund, warum Java und C # immer das gleiche Mem "String bedeutet Unicode" hatten ... Vielleicht etwas Overhead, aber VIEL bessere Unterstützung für fast alles andere als Englisch! -).
Alex Martelli
29
x.translate(None, string.digits)führt tatsächlich dazu 'aaabbbbbb', was das Gegenteil von dem ist, was beabsichtigt ist.
Tom Dalling
4
In Anlehnung an die Kommentare von Tom Dalling enthält Ihr erstes Beispiel alle unerwünschten Zeichen - macht das Gegenteil von dem, was Sie gesagt haben.
Chris Johnson
3
@ RyanB.Lynch et al., Der Fehler lag bei einem späteren Editor und zwei anderen Benutzern, die diese Bearbeitung genehmigt haben , was in der Tat völlig falsch ist. Zurückgesetzt.
Nick T
1
übergeordnetes alleingebautes ... da bin ich mir nicht sicher!
Andy Hayden
197

Verwenden Sie re.subwie folgt:

>>> import re
>>> re.sub('\D', '', 'aas30dsa20')
'3020'

\D Entspricht einem nichtstelligen Zeichen, ersetzt der obige Code im Wesentlichen jedes nichtstellige Zeichen für die leere Zeichenfolge.

Oder Sie können filterwie folgt verwenden (in Python 2):

>>> filter(str.isdigit, 'aas30dsa20')
'3020'

Da in Python 3 filterein Iterator anstelle von a zurückgegeben wird list, können Sie stattdessen Folgendes verwenden:

>>> ''.join(filter(str.isdigit, 'aas30dsa20'))
'3020'
João Silva
quelle
Es ist böse in solch einer einfachen Aufgabe, die zweite ist die beste, die ich denke, weil 'ist ...' Methoden für Strings am schnellsten sind.
f0b0s
Ihr Filterbeispiel ist auf py2k
SilentGhost
2
@ f0b0s-iu9-info: hast du es geplant? auf meiner Maschine (py3k) ist re doppelt so schnell wie Filter mit isdigit, Generator mit isdigtist auf halbem Weg dazwischen
SilentGhost
@ SilentGhost: Danke, ich habe IDLE von py2k verwendet. Es ist jetzt behoben.
João Silva
1
@ Plasmaier Einfach rfür rohe Saite verwenden:re.sub(r"\D+", "", "aas30dsa20")
Mitch McMabers
64
s=''.join(i for i in s if i.isdigit())

Eine weitere Generatorvariante.

f0b0s
quelle
Killed it .. + 1 Wäre noch besser gewesen, wenn Lamda verwendet worden wäre
Barath Ravikumar
17

Sie können Filter verwenden:

filter(lambda x: x.isdigit(), "dasdasd2313dsa")

Auf python3.0 musst du dich dem anschließen (irgendwie hässlich :()

''.join(filter(lambda x: x.isdigit(), "dasdasd2313dsa"))
Freiksenet
quelle
nur in py2k, in py3k gibt es einen Generator zurück
SilentGhost
konvertieren strzu list, um sicherzustellen, dass es sowohl auf py2 als auch auf py3 funktioniert:''.join(filter(lambda x: x.isdigit(), list("dasdasd2313dsa")))
Luiz C.
13

in Anlehnung an Bayers Antwort:

''.join(i for i in s if i.isdigit())
SilentGhost
quelle
Nein, dies funktioniert bei negativen Zahlen nicht, da -es sich nicht um eine Ziffer handelt.
Oli
12

Sie können dies einfach mit Regex tun

>>> import re
>>> re.sub("\D","","£70,000")
70000
Aminah Nuraini
quelle
Bei weitem der einfachste Weg
Iorek
5
Wie unterscheidet sich das von João Silvas Antwort, die 7 Jahre zuvor gegeben wurde?
JWW
7
x.translate(None, string.digits)

löscht alle Ziffern aus der Zeichenfolge. Gehen Sie folgendermaßen vor, um Buchstaben zu löschen und die Ziffern beizubehalten:

x.translate(None, string.letters)
Terje Molnes
quelle
3
Ich bekomme ein TypeError: translate () nimmt genau ein Argument (2 angegeben). Warum diese Frage in ihrem gegenwärtigen Zustand positiv bewertet wurde, ist ziemlich frustrierend.
Bobort
Die Übersetzung wurde von Python 2 in 3 geändert. Die Syntax, die diese Methode in Python 3 verwendet, lautet x.translate (str.maketrans ('', '', string.digits)) und x.translate (str.maketrans ('', ''). , string.ascii_letters)). Keiner dieser Streifen Leerraum. Ich würde diesen Ansatz nicht mehr wirklich empfehlen ...
ZaxR
5

Die Op erwähnt in den Kommentaren, dass er die Dezimalstelle behalten möchte. Dies kann mit der Methode re.sub (gemäß der zweiten und meiner Meinung nach besten Antwort) erfolgen, indem die zu behaltenden Zeichen explizit aufgelistet werden, z

>>> re.sub("[^0123456789\.]","","poo123.4and5fish")
'123.45'
Roger Heathcote
quelle
Was ist mit "poo123.4and.5fish"?
Jan Tojnar
In meinem Code überprüfe ich die Anzahl der Punkte in der Eingabezeichenfolge und erhalte einen Fehler, wenn dieser mehr als 1 beträgt.
Roger Heathcote
4

Eine schnelle Version für Python 3:

# xx3.py
from collections import defaultdict
import string
_NoneType = type(None)

def keeper(keep):
    table = defaultdict(_NoneType)
    table.update({ord(c): c for c in keep})
    return table

digit_keeper = keeper(string.digits)

Hier ist ein Leistungsvergleich mit Regex:

$ python3.3 -mtimeit -s'import xx3; x="aaa12333bb445bb54b5b52"' 'x.translate(xx3.digit_keeper)'
1000000 loops, best of 3: 1.02 usec per loop
$ python3.3 -mtimeit -s'import re; r = re.compile(r"\D"); x="aaa12333bb445bb54b5b52"' 'r.sub("", x)'
100000 loops, best of 3: 3.43 usec per loop

Für mich ist es also etwas mehr als dreimal schneller als Regex. Es ist auch schneller als class Deloben, da defaultdictalle Suchvorgänge in C und nicht in (langsamem) Python ausgeführt werden. Hier ist diese Version auf meinem System zum Vergleich.

$ python3.3 -mtimeit -s'import xx; x="aaa12333bb445bb54b5b52"' 'x.translate(xx.DD)'
100000 loops, best of 3: 13.6 usec per loop
residdsk
quelle
3

Verwenden Sie einen Generatorausdruck:

>>> s = "foo200bar"
>>> new_s = "".join(i for i in s if i in "0123456789")
Bayer
quelle
stattdessen tun''.join(n for n in foo if n.isdigit())
shxfee
2

Hässlich aber funktioniert:

>>> s
'aaa12333bb445bb54b5b52'
>>> a = ''.join(filter(lambda x : x.isdigit(), s))
>>> a
'1233344554552'
>>>
Gant
quelle
warum tun Sie list(s)?
SilentGhost
@ SilentGhost es ist mein Missverständnis. hatte es korrigiert danke :)
Gant
Eigentlich glaube ich nicht, dass Sie mit dieser Methode "join" verwenden müssen. filter(lambda x: x.isdigit(), s)hat gut für mich funktioniert. ... oh, das liegt daran, dass ich Python 2.7 verwende.
Bobort
1
$ python -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'

100000 Schleifen, am besten 3: 2,48 usec pro Schleife

$ python -mtimeit -s'import re; x="aaa12333bab445bb54b5b52"' '"".join(re.findall("[a-z]+",x))'

100000 Schleifen, am besten 3: 2,02 usec pro Schleife

$ python -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'

100000 Schleifen, am besten 3: 2,37 usec pro Schleife

$ python -mtimeit -s'import re; x="aaa12333bab445bb54b5b52"' '"".join(re.findall("[a-z]+",x))'

100000 Schleifen, am besten 3: 1,97 usec pro Schleife

Ich hatte beobachtet, dass Join schneller als Sub ist.

AnilReddy
quelle
Warum wiederholen Sie die beiden Methoden zweimal? Und können Sie beschreiben, wie sich Ihre Antwort von der akzeptierten unterscheidet?
Jan Tojnar
Beide ergeben die gleiche Ausgabe. Aber ich möchte nur zeigen, dass Join die Submethode in den Ergebnissen schneller ist.
AnilReddy
Sie tun es nicht, Ihr Code macht das Gegenteil. Und Sie haben auch vier Messungen, aber nur zwei Methoden.
Jan Tojnar
1

Sie können jedes Zeichen lesen. Wenn es sich um eine Ziffer handelt, nehmen Sie sie in die Antwort auf. Mit dieser str.isdigit() Methode können Sie feststellen, ob ein Zeichen eine Ziffer ist.

your_input = '12kjkh2nnk34l34'
your_output = ''.join(c for c in your_input if c.isdigit())
print(your_output) # '1223434'
Yoelvis
quelle
Wie unterscheidet sich das von der Antwort von f0b0s? Sie sollten diese Antwort stattdessen bearbeiten, wenn Sie weitere Informationen mitbringen
möchten
0

Kein Einzeiler, aber sehr einfach:

buffer = ""
some_str = "aas30dsa20"

for char in some_str:
    if not char.isdigit():
        buffer += char

print( buffer )
Josh
quelle
0

Ich habe das benutzt. 'letters'sollte alle Buchstaben enthalten, die Sie loswerden möchten:

Output = Input.translate({ord(i): None for i in 'letters'}))

Beispiel:

Input = "I would like 20 dollars for that suit" Output = Input.translate({ord(i): None for i in 'abcdefghijklmnopqrstuvwxzy'})) print(Output)

Ausgabe: 20

Gustav
quelle