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.