Usunięcie wszystkich znaków nienumerycznych ze stringów w Pythonie

Odpowiedzi:

267
>>> import re
>>> re.sub("[^0-9]", "", "sdkjh987978asd098as0980a98sd")
'987978098098098'
Ned Batchelder
źródło
90
to może być re.sub (r "\ D", "", "sdkjh987978asd098as0980a98sd")
newacct
3
a to może być: z ponownego importu sub
James Koss
90

Nie jestem pewien, czy jest to najbardziej efektywny sposób, ale:

>>> ''.join(c for c in "abc123def456" if c.isdigit())
'123456'

Te ''.joinśrodki część połączyć wszystkie wynikowe znaki razem bez żadnych znaków pomiędzy. Reszta jest listą, w której (jak można się domyślić) bierzemy tylko te części ciągu, które pasują do warunku isdigit.

Mark Rushakoff
źródło
1
To odwrotnie. Myślę, że masz na myśli „nie c.isdigit ()”
Ryan R. Rosario
7
Usuń wszystkie nieliczbowe == zachowaj tylko wartości liczbowe.
Mark Rushakoff
10
Podoba mi się, że to podejście nie wymaga wciągania w tę prostą funkcję.
triunatura
Zauważ, że w przeciwieństwie do implementacji używających str.translate, to rozwiązanie działa zarówno w Pythonie 2.7 i 3.4. Dziękuję Ci!
Alex
1
Wolę tę alternatywę. Używanie wyrażenia regularnego wydaje mi się przesadą.
alfredocambera
18

Powinno to zadziałać zarówno dla ciągów znaków, jak i obiektów Unicode w Python2 oraz dla ciągów i bajtów w Python3:

# python <3.0
def only_numerics(seq):
    return filter(type(seq).isdigit, seq)

# python ≥3.0
def only_numerics(seq):
    seq_type= type(seq)
    return seq_type().join(filter(seq_type.isdigit, seq))
tzot
źródło
9

Aby dodać kolejną opcję do miksu, w stringmodule jest kilka przydatnych stałych . Chociaż są bardziej przydatne w innych przypadkach, można ich tutaj użyć.

>>> from string import digits
>>> ''.join(c for c in "abc123def456" if c in digits)
'123456'

W module jest kilka stałych, w tym:

  • ascii_letters (abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ)
  • hexdigits (0123456789abcdefABCDEF)

Jeśli intensywnie używasz tych stałych, warto zamienić je na plik frozenset. To umożliwia wyszukiwanie O (1) zamiast O (n), gdzie n jest długością stałej dla oryginalnych ciągów.

>>> digits = frozenset(digits)
>>> ''.join(c for c in "abc123def456" if c in digits)
'123456'
Tim McNamara
źródło
'' .join (c for c in "abc123def456", jeśli c.isdigit ()) działa w moim Pythonie 3.4
Eino Mäkitalo
7

@Ned Batchelder i @newacct udzielili właściwej odpowiedzi, ale ...

Na wszelki wypadek, jeśli masz przecinek (,) dziesiętnie (.) W swoim ciągu:

import re
re.sub("[^\d\.]", "", "$1,999,888.77")
'1999888.77'
kennyut
źródło
5

Najszybszym podejściem, jeśli musisz wykonać więcej niż jedną lub dwie takie operacje usuwania (lub nawet jedną, ale na bardzo długim łańcuchu! -), jest poleganie na translatemetodzie ciągów, nawet jeśli wymaga to przygotowania:

>>> import string
>>> allchars = ''.join(chr(i) for i in xrange(256))
>>> identity = string.maketrans('', '')
>>> nondigits = allchars.translate(identity, string.digits)
>>> s = 'abc123def456'
>>> s.translate(identity, nondigits)
'123456'

translateMetoda jest inna i może odrobinę prostsze prostsze w użyciu, na Unicode, niż jest na ciągi bajtów, btw:

>>> unondig = dict.fromkeys(xrange(65536))
>>> for x in string.digits: del unondig[ord(x)]
... 
>>> s = u'abc123def456'
>>> s.translate(unondig)
u'123456'

Możesz chcieć użyć klasy mapowania zamiast faktycznego dyktu, zwłaszcza jeśli twój łańcuch Unicode może potencjalnie zawierać znaki o bardzo wysokich wartościach ord (które spowodowałyby, że dykt byłby zbyt duży ;-). Na przykład:

>>> class keeponly(object):
...   def __init__(self, keep): 
...     self.keep = set(ord(c) for c in keep)
...   def __getitem__(self, key):
...     if key in self.keep:
...       return key
...     return None
... 
>>> s.translate(keeponly(string.digits))
u'123456'
>>> 
Alex Martelli
źródło
2
(1) Nie zapisuj na stałe magicznych liczb; s / 65536 / sys.maxunicode / (2) Dykt jest bezwarunkowo „nadmiernie duży”, ponieważ wejście „może potencjalnie” zawierać (sys.maxunicode - number_of_non_numeric_chars)wpisy. (3) rozważ, czy string.digits może być niewystarczające, co prowadzi do konieczności złamania modułu unicodedata (4) rozważ re.sub (r '(? U) \ D +', u '', text) dla uproszczenia i potencjału prędkość.
John Machin
2

Wiele poprawnych odpowiedzi, ale na wypadek, gdybyś chciał, aby to było zmiennoprzecinkowe, bezpośrednio, bez użycia wyrażenia regularnego:

x= '$123.45M'

float(''.join(c for c in x if (c.isdigit() or c =='.'))

123,45

Możesz zmienić punkt przecinka w zależności od potrzeb.

zmień to, jeśli wiesz, że Twój numer jest liczbą całkowitą

x='$1123'    
int(''.join(c for c in x if c.isdigit())

1123

Alberto Ibarra
źródło