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


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'

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.


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

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

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

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)

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'

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

3

Użyj wyrażenia generatora:

>>> s = "foo200bar"
>>> new_s = "".join(i for i in s if i in "0123456789")

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

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.


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'

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 )

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

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.