Rimuovere i caratteri tranne le cifre dalla stringa usando Python?


137

Come posso rimuovere tutti i caratteri tranne i numeri dalla stringa?


@Jan Tojnar: puoi fare un esempio?
João Silva,

@JG: Ho gtk.Entry () e voglio che il float moltiplicato sia inserito in esso.
Jan Tojnar,

1
@JanTojnar usa il metodo re.sub come da risposta due ed elenca esplicitamente quali caratteri mantenere ad esempio re.sub ("[^ 0123456789 \.]", "", "Poo123.4and5fish")
Roger Heathcote il

Risposte:


112

In Python 2. *, l'approccio di gran lunga più veloce è il .translatemetodo:

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

string.maketranscrea una tabella di traduzione (una stringa di lunghezza 256) che in questo caso è la stessa ''.join(chr(x) for x in range(256))(solo più veloce da realizzare ;-). .translateapplica la tabella di traduzione (che qui è irrilevante poiché allessenzialmente significa identità) E cancella i caratteri presenti nel secondo argomento - la parte chiave.

.translatefunziona in modo molto diverso sulle stringhe Unicode (e sulle stringhe in Python 3 - I do domande dei desideri specificare che major release di Python è di interesse!) - non del tutto questo semplice, non del tutto così veloce, anche se ancora abbastanza usabile.

Torna a 2. *, la differenza di prestazioni è impressionante ...:

$ 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

Accelerare le cose di 7-8 volte non è certo arachidi, quindi translatevale la pena conoscere e utilizzare il metodo. L'altro approccio non-RE popolare ...:

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

è il 50% più lento di RE, quindi l' .translateapproccio lo supera di un ordine di grandezza.

In Python 3, o per Unicode, è necessario passare .translateuna mappatura (con ordinali, non direttamente i caratteri, come chiavi) che ritorna Noneper ciò che si desidera eliminare. Ecco un modo conveniente per esprimere questo per la cancellazione di "tutto tranne" alcuni caratteri:

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)

emette anche '1233344554552'. Tuttavia, inserendo questo in xx.py abbiamo ...:

$ 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

... che mostra il vantaggio in termini di prestazioni scompare, per questo tipo di attività di "eliminazione", e diventa una riduzione delle prestazioni.


1
@sunqiang, sì, assolutamente - c'è una ragione per cui Py3k è passato a Unicode come IL tipo di stringa di testo, invece delle stringhe di byte come in Py2 - lo stesso motivo per cui Java e C # hanno sempre avuto lo stesso meme "stringa significa unicode" ... un po 'di sovraccarico, forse, ma MOLTO migliore supporto per qualsiasi cosa tranne l'inglese! -).
Alex Martelli,

29
x.translate(None, string.digits)in realtà risulta 'aaabbbbbb', che è l'opposto di ciò che si intende.
Tom Dalling,

4
Facendo eco ai commenti di Tom Dalling, il tuo primo esempio mantiene tutti i personaggi indesiderati - fa il contrario di quello che hai detto.
Chris Johnson,

3
@ RyanB.Lynch et al., La colpa era di un editore successivo e di altri due utenti che hanno approvato la modifica , che, in effetti, è totalmente sbagliata. Ritornato.
Nick T

1
scavalcamento allincorporato ... non ne sono sicuro!
Andy Hayden,

197

Utilizzare in questo re.submodo:

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

\D corrisponde a qualsiasi carattere non numerico, pertanto il codice sopra riportato sostituisce essenzialmente ogni carattere non numerico per la stringa vuota.

Oppure puoi usare filter, in questo modo (in Python 2):

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

Poiché in Python 3, filterrestituisce un iteratore anziché un list, puoi invece utilizzare quanto segue:

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

re è malvagio in un compito così semplice, il secondo è il migliore che penso, perché i metodi "è ..." sono i più veloci per le stringhe.
f0b0s

il tuo esempio di filtro è limitato a py2k
SilentGhost,

2
@ f0b0s-iu9-info: l'hai cronometrato? sulla mia macchina (py3k) re è due volte più veloce del filtro con isdigit, il generatore con isdigtè a metà strada tra loro
SilentGhost

@SilentGhost: grazie, stavo usando IDLE da py2k. Ora è riparato.
João Silva,

1
@asmaier Basta usare rper stringa grezza:re.sub(r"\D+", "", "aas30dsa20")
Mitch McMabers

64
s=''.join(i for i in s if i.isdigit())

Un'altra variante del generatore.


Ucciso ... + 1 Sarebbe stato ancora meglio se fosse stato usato lamda
Barath Ravikumar

17

Puoi usare il filtro:

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

Su python3.0 devi unirti a questo (un pò brutto :()

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

solo in py2k, in py3k restituisce un generatore
SilentGhost,

converti strin listper assicurarti che funzioni sia su py2 che su py3:''.join(filter(lambda x: x.isdigit(), list("dasdasd2313dsa")))
Luiz C.

13

sulla falsariga della risposta di bayer:

''.join(i for i in s if i.isdigit())

No, questo non funzionerà con numeri negativi perché -non è una cifra.
Oli,

12

Puoi farlo facilmente usando Regex

>>> import re
>>> re.sub("\D","","£70,000")
70000

Di gran lunga il modo più semplice
Iorek,

5
In che modo differisce dalla risposta di João Silva, che è stata fornita 7 anni prima?
1919

7
x.translate(None, string.digits)

cancellerà tutte le cifre dalla stringa. Per eliminare le lettere e conservare le cifre, procedere come segue:

x.translate(None, string.letters)

3
Ottengo un TypeError: translate () accetta esattamente un argomento (2 dato). Perché questa domanda sia stata votata nel suo stato attuale è abbastanza frustrante.
Bobort,

translate modificato da python 2 a 3. La sintassi che utilizza questo metodo in python 3 è x.translate (str.maketrans ('', '', string.digits)) e x.translate (str.maketrans ('', '' , string.ascii_letters)). Nessuna di queste strisce di spazio bianco. Non consiglierei più questo approccio ...
ZaxR

5

L'op menziona nei commenti che vuole mantenere il decimale. Questo può essere fatto con il metodo re.sub (secondo il secondo e la migliore risposta IMHO) elencando esplicitamente i caratteri da conservare ad es.

>>> re.sub("[^0123456789\.]","","poo123.4and5fish")
'123.45'

Che dire di "poo123.4and.5fish"?
Jan Tojnar,

Nel mio codice controllo il numero di punti nella stringa di input e sollevo un errore se è superiore a 1.
Roger Heathcote,

4

Una versione veloce per 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)

Ecco un confronto delle prestazioni contro 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

Quindi per me è un po 'più di 3 volte più veloce di regex. È anche più veloce di class Delsopra, perché defaultdictesegue tutte le sue ricerche in C, piuttosto che (lento) Python. Ecco quella versione sul mio stesso sistema, per confronto.

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

3

Usa un'espressione del generatore:

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

invece fai''.join(n for n in foo if n.isdigit())
shxfee il

2

Brutto ma funziona:

>>> s
'aaa12333bb445bb54b5b52'
>>> a = ''.join(filter(lambda x : x.isdigit(), s))
>>> a
'1233344554552'
>>>

perchè lo fai list(s)?
SilentGhost,

@SilentGhost è il mio fraintendimento. lo ha corretto grazie :)
Gant,

In realtà, con questo metodo, non penso che tu debba usare "join". filter(lambda x: x.isdigit(), s)ha funzionato bene per me. ... oh, è perché sto usando Python 2.7.
Bobort,

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

100000 loop, meglio di 3: 2,48 usec per loop

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

100000 loop, meglio di 3: 2,02 usec per loop

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

100000 loop, meglio di 3: 2,37 usec per loop

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

100000 loop, meglio di 3: 1,97 usec per loop

Avevo osservato che il join è più veloce del sub.


Perché stai ripetendo i due metodi due volte? E potresti descrivere in che modo la tua risposta è diversa da quella accettata?
Jan Tojnar,

Entrambi danno lo stesso risultato. Ma voglio solo mostrare che l'unione è più veloce il metodo secondario nei risultati.
AnilReddy,

Non lo fanno, il tuo codice fa il contrario. E anche tu hai quattro misure ma solo due metodi.
Jan Tojnar,

1

Puoi leggere ogni personaggio. Se è una cifra, includila nella risposta. Il str.isdigit() metodo è un modo per sapere se un carattere è una cifra.

your_input = '12kjkh2nnk34l34'
your_output = ''.join(c for c in your_input if c.isdigit())
print(your_output) # '1223434'

in cosa differisce dalla risposta di f0b0s? Dovresti modificare questa risposta invece se hai ulteriori informazioni da portare
chevybow

0

Non una fodera ma molto semplice:

buffer = ""
some_str = "aas30dsa20"

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

print( buffer )

0

Ho usato questo 'letters'dovrebbe contenere tutte le lettere di cui vuoi sbarazzarti:

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

Esempio:

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

Produzione: 20

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.