Usunąć znaki z wyjątkiem cyfr z ciągu za pomocą Pythona?

137

Jak mogę usunąć wszystkie znaki z wyjątkiem liczb z ciągu?

Jan Tojnar
źródło
@Jan Tojnar: Czy możesz podać przykład?
João Silva
@JG: Mam gtk.Entry () i chcę wprowadzić do niego multiply float.
Jan Tojnar
1
@JanTojnar używa metody re.sub zgodnie z odpowiedzią 2 i wyraźnie podaje, które znaki mają być zachowane, np. Re.sub ("[^ 0123456789 \.]", "", "Poo123.4and5fish")
Roger Heathcote

Odpowiedzi:

112

W Pythonie 2. * zdecydowanie najszybszym podejściem jest .translatemetoda:

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

string.maketranstworzy tablicę tłumaczeń (ciąg o długości 256), która w tym przypadku jest taka sama jak ''.join(chr(x) for x in range(256))(tylko szybsza do wykonania ;-). .translatestosuje tabelę tłumaczeń (która tutaj nie ma znaczenia, ponieważ allzasadniczo oznacza tożsamość) ORAZ usuwa znaki obecne w drugim argumencie - części kluczowej.

.translatedziała bardzo różnie na Unicode (i smyczki w Pythonie 3 - I zrobić pytania życzenie określony których głównym uwalnianiu Pythona ma znaczenie!) - nie całkiem to proste, to nie dość szybko, choć nadal bardzo użyteczny.

Wracając do 2. *, różnica w wydajności jest imponująca ...:

$ 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

Przyspieszenie tempa 7-8 razy to nie grosze, więc translatewarto znać i stosować tę metodę. Inne popularne podejście non-RE ...:

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

jest o 50% wolniejszy niż RE, więc .translatepodejście przebija go o ponad rząd wielkości.

W Pythonie 3 lub w Unicode musisz przekazać .translateodwzorowanie (z liczbami porządkowymi, a nie znakami bezpośrednio, jako klucze), które zwraca Noneto, co chcesz usunąć. Oto wygodny sposób wyrażenia tego w celu usunięcia „wszystkiego oprócz” kilku znaków:

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)

również emituje '1233344554552'. Jednak umieszczając to w xx.py mamy ...:

$ 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

... co pokazuje, że przewaga wydajności znika w przypadku tego rodzaju zadań „usuwania” i staje się spadkiem wydajności.

Alex Martelli
źródło
1
@sunqiang, tak, absolutnie - istnieje powód, dla którego Py3k przeszedł na Unicode jako typ ciągu tekstowego, zamiast ciągów bajtów, jak w Py2 - ten sam powód, dla którego Java i C # zawsze miały ten sam mem "ciąg znaków oznacza unicode" ... może trochę narzutów, ale DUŻO lepsze wsparcie dla prawie wszystkiego oprócz angielskiego! -).
Alex Martelli
29
x.translate(None, string.digits)w rzeczywistości skutkuje 'aaabbbbbb', co jest przeciwieństwem tego, co jest zamierzone.
Tom Dalling
4
Powtarzając komentarze Toma Dallinga, twój pierwszy przykład zachowuje wszystkie niepożądane postacie - robi odwrotność tego, co powiedziałeś.
Chris Johnson,
3
@ RyanB.Lynch i in., Wina była spowodowana późniejszym redaktorem i dwoma innymi użytkownikami, którzy zatwierdzili tę zmianę , co w rzeczywistości jest całkowicie błędne. Przywrócony.
Nick T
1
nadpisywanie allwbudowanego ... nie jestem tego pewien!
Andy Hayden
197

Użyj re.sub, tak jak to:

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

\D dopasowuje dowolny znak niecyfrowy, więc powyższy kod zasadniczo zastępuje każdy znak niecyfrowy w pustym łańcuchu.

Lub możesz użyć filter, na przykład (w Pythonie 2):

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

Ponieważ w Pythonie 3 filterzwraca iterator zamiast alist , możesz zamiast tego użyć następującego:

>>> ''.join(filter(str.isdigit, 'aas30dsa20'))
'3020'
João Silva
źródło
re jest zło ​​w tak prostym zadaniu, drugie jest najlepsze, jak sądzę, ponieważ metody „jest…” są najszybsze dla strun.
f0b0s
Twój przykład filtru jest ograniczony do py2k
SilentGhost
2
@ f0b0s-iu9-info: czy to zaplanowałeś? na moim komputerze (py3k) re jest dwa razy szybszy niż filtr z isdigit, generator w isdigtpołowie drogi między nimi
SilentGhost
@SilentGhost: Dzięki, korzystałem z IDLE z py2k. To jest teraz naprawione.
João Silva
1
@asmaier Po prostu użyj rdla nieprzetworzonego ciągu:re.sub(r"\D+", "", "aas30dsa20")
Mitch McMabers
64
s=''.join(i for i in s if i.isdigit())

Inny wariant generatora.

f0b0s
źródło
Zabił go ... + 1 Byłoby jeszcze lepiej, gdyby użyto
lamdy
17

Możesz użyć filtra:

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

Na pythonie 3.0 musisz dołączyć do tego (trochę brzydkie :()

''.join(filter(lambda x: x.isdigit(), "dasdasd2313dsa"))
freiksenet
źródło
tylko w py2k, w py3k zwraca generator
SilentGhost
przekonwertować strna, listaby upewnić się, że działa zarówno na py2, jak i py3:''.join(filter(lambda x: x.isdigit(), list("dasdasd2313dsa")))
Luiz C.
13

zgodnie z odpowiedzią Bayera:

''.join(i for i in s if i.isdigit())
SilentGhost
źródło
Nie, to nie zadziała dla liczb ujemnych, ponieważ -nie jest cyfrą.
Oli
12

Możesz to łatwo zrobić za pomocą Regex

>>> import re
>>> re.sub("\D","","£70,000")
70000
Aminah Nuraini
źródło
Zdecydowanie najłatwiejszy sposób
Iorek
5
Czym różni się to od odpowiedzi João Silvy, która została udzielona 7 lat wcześniej?
jww
7
x.translate(None, string.digits)

usunie wszystkie cyfry z ciągu. Aby usunąć litery i zachować cyfry, wykonaj następujące czynności:

x.translate(None, string.letters)
Terje Molnes
źródło
3
Otrzymuję TypeError: translate () przyjmuje dokładnie jeden argument (podane 2). Dlaczego głosowano za tym pytaniem w obecnym stanie, jest dość frustrujące.
Bobort
translate zmieniono z pythona 2 na 3. Składnia używająca tej metody w pythonie 3 to x.translate (str.maketrans ('', '', string.digits)) i x.translate (str.maketrans ('', '' , string.ascii_letters)). Żaden z tych pasków nie jest biały. Nie polecałbym już tego podejścia ...
ZaxR
5

Opcja wspomina w komentarzach, że chce zachować miejsce dziesiętne. Można to zrobić za pomocą metody re.sub (zgodnie z drugą i najlepszą odpowiedzią IMHO), jawnie wymieniając znaki, które mają być zachowane, np.

>>> re.sub("[^0123456789\.]","","poo123.4and5fish")
'123.45'
Roger Heathcote
źródło
A co z „poo123.4and.5fish”?
Jan Tojnar
W moim kodzie sprawdzam liczbę okresów w ciągu wejściowym i zgłaszam błąd, jeśli jest więcej niż 1.
Roger Heathcote
4

Szybka wersja dla Pythona 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)

Oto porównanie wydajności z wyrażeniem regularnym:

$ 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

Jak dla mnie jest to trochę ponad 3 razy szybsze niż regex. Jest również szybszy niż class Delpowyżej, ponieważ defaultdictwszystkie wyszukiwania wykonuje w C, a nie (powolny) Python. Oto ta wersja w moim systemie dla porównania.

$ python3.3 -mtimeit -s'import xx; x="aaa12333bb445bb54b5b52"' 'x.translate(xx.DD)'
100000 loops, best of 3: 13.6 usec per loop
rescdsk
źródło
3

Użyj wyrażenia generatora:

>>> s = "foo200bar"
>>> new_s = "".join(i for i in s if i in "0123456789")
Bayer
źródło
zamiast tego zrób''.join(n for n in foo if n.isdigit())
shxfee,
2

Brzydki, ale działa:

>>> s
'aaa12333bb445bb54b5b52'
>>> a = ''.join(filter(lambda x : x.isdigit(), s))
>>> a
'1233344554552'
>>>
Gant
źródło
dlaczego to robisz list(s)?
SilentGhost
@SilentGhost to moje nieporozumienie. miał to poprawione dzięki :)
Gant
Właściwie przy tej metodzie nie sądzę, abyś musiał używać opcji „dołącz”. filter(lambda x: x.isdigit(), s)działało dobrze dla mnie. ... och, to dlatego, że używam Pythona 2.7.
Bobort
1
$ python -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'

100000 pętli, najlepiej 3: 2,48 usek na pętlę

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

100000 pętli, najlepiej 3: 2,02 usek na pętlę

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

100000 pętli, najlepiej 3: 2,37 usek na pętlę

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

100000 pętli, najlepiej 3: 1,97 usek na pętlę

Zauważyłem, że łączenie jest szybsze niż sub.

AnilReddy
źródło
Dlaczego dwukrotnie powtarzasz te dwie metody? Czy mógłbyś opisać, czym różni się Twoja odpowiedź od zaakceptowanej?
Jan Tojnar
Oba dają ten sam wynik. Ale chcę tylko pokazać, że łączenie jest szybszą metodą podrzędną w wynikach.
AnilReddy,
Nie, twój kod działa odwrotnie. Masz również cztery pomiary, ale tylko dwie metody.
Jan Tojnar
1

Możesz przeczytać każdą postać. Jeśli jest cyfrą, dołącz ją do odpowiedzi. str.isdigit() Metoda jest sposobem, aby wiedzieć, czy znak jest cyfra.

your_input = '12kjkh2nnk34l34'
your_output = ''.join(c for c in your_input if c.isdigit())
print(your_output) # '1223434'
yoelvis
źródło
czym różni się to od odpowiedzi przez f0b0s? Zamiast tego powinieneś edytować tę odpowiedź, jeśli masz więcej informacji do przekazania
chevybow
0

Nie jest to jedna wkładka, ale bardzo prosta:

buffer = ""
some_str = "aas30dsa20"

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

print( buffer )
Josh
źródło
0

Użyłem tego. 'letters'powinien zawierać wszystkie litery, których chcesz się pozbyć:

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

Przykład:

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

Wynik: 20

Gustav
źródło