Supprimer les caractères sauf les chiffres de la chaîne en utilisant Python?

137

Comment puis-je supprimer tous les caractères sauf les nombres de la chaîne?

Jan Tojnar
la source
@Jan Tojnar: Pouvez-vous donner un exemple?
João Silva
@JG: J'ai gtk.Entry () et je veux que le multiply float y entre.
Jan Tojnar
1
@JanTojnar utilise la méthode re.sub selon la deuxième réponse et liste explicitement les caractères à conserver, par exemple re.sub ("[^ 0123456789 \.]", "", "Poo123.4and5fish")
Roger Heathcote

Réponses:

112

En Python 2. *, l'approche de loin la plus rapide est la .translateméthode:

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

string.maketransfait une table de traduction (une chaîne de longueur 256) qui dans ce cas est la même que ''.join(chr(x) for x in range(256))(juste plus rapide à faire ;-). .translateapplique la table de traduction (qui ici n'est pas pertinente car allsignifie essentiellement identité) ET supprime les caractères présents dans le deuxième argument - la partie clé.

.translatefonctionne très différemment sur les chaînes Unicode (et les chaînes en Python 3 - Je fais des questions de souhaits spécifiques , qui majeure à libération de Python est intéressant!) - pas tout à fait ce simple, pas tout à fait ce jeûne, mais encore tout à fait utilisable.

De retour à 2. *, la différence de performances est impressionnante ...:

$ 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

Accélérer les choses de 7 à 8 fois n'est guère d'arachides, donc la translateméthode vaut la peine d'être connue et utilisée. L'autre approche non-ER populaire ...:

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

est 50% plus lent que RE, donc l' .translateapproche la bat de plus d'un ordre de grandeur.

Dans Python 3, ou pour Unicode, vous devez passer .translateun mappage (avec des ordinaux, et non des caractères directement, comme clés) qui renvoie Nonece que vous voulez supprimer. Voici un moyen pratique d'exprimer cela pour supprimer "tout sauf" quelques caractères:

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)

émet également '1233344554552'. Cependant, en mettant cela dans xx.py, nous avons ...:

$ 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

... ce qui montre que l'avantage de performance disparaît, pour ce genre de tâches de «suppression», et devient une diminution des performances.

Alex Martelli
la source
1
@sunqiang, oui, absolument - il y a une raison pour laquelle Py3k est passé à Unicode en tant que type de chaîne de texte, au lieu de chaînes d'octets comme dans Py2 - même raison pour laquelle Java et C # ont toujours eu le même mème "string means unicode" ... des frais généraux, peut-être, mais BEAUCOUP meilleur support pour à peu près tout sauf l'anglais! -).
Alex Martelli
29
x.translate(None, string.digits)aboutit en fait 'aaabbbbbb', ce qui est le contraire de ce qui est prévu.
Tom Dalling
4
Faisant écho aux commentaires de Tom Dalling, votre premier exemple conserve tous les caractères indésirables - fait le contraire de ce que vous avez dit.
Chris Johnson le
3
@ RyanB.Lynch et al, la faute était avec un éditeur plus récent et deux autres utilisateurs qui ont approuvé cette modification , ce qui, en fait, est totalement faux. Revenu.
Nick T
1
remplaçant allbuiltin ... pas sûr!
Andy Hayden
197

Utilisez re.sub, comme ceci:

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

\D correspond à n'importe quel caractère non numérique, donc le code ci-dessus remplace essentiellement chaque caractère non numérique de la chaîne vide.

Ou vous pouvez utiliser filter, comme ça (en Python 2):

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

Puisque dans Python 3, filterrenvoie un itérateur au lieu de a list, vous pouvez utiliser ce qui suit à la place:

>>> ''.join(filter(str.isdigit, 'aas30dsa20'))
'3020'
João Silva
la source
re est mal dans une tâche aussi simple, la deuxième est la meilleure que je pense, car les méthodes 'est ...' sont les plus rapides pour les cordes.
f0b0s
votre exemple de filtre est limité à py2k
SilentGhost
2
@ f0b0s-iu9-info: l'avez-vous chronométré? sur ma machine (py3k) re est deux fois plus rapide que le filtre avec isdigit, le générateur avec isdigtest à mi-chemin entre eux
SilentGhost
@SilentGhost: Merci, j'utilisais IDLE de py2k. C'est réglé maintenant.
João Silva
1
@asmaier Simplement utiliser rpour la chaîne brute:re.sub(r"\D+", "", "aas30dsa20")
Mitch McMabers
64
s=''.join(i for i in s if i.isdigit())

Une autre variante de générateur.

f0b0s
la source
Tué .. + 1 Cela aurait été encore mieux si lamda avait été utilisé
Barath Ravikumar
17

Vous pouvez utiliser le filtre:

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

Sur python3.0, vous devez rejoindre ceci (un peu moche :()

''.join(filter(lambda x: x.isdigit(), "dasdasd2313dsa"))
freiksenet
la source
seulement dans py2k, dans py3k, il renvoie un générateur
SilentGhost
convertir stren listpour vous assurer qu'il fonctionne à la fois sur py2 et py3:''.join(filter(lambda x: x.isdigit(), list("dasdasd2313dsa")))
Luiz C.
13

dans le sens de la réponse de Bayer:

''.join(i for i in s if i.isdigit())
SilentGhost
la source
Non, cela ne fonctionnera pas pour les nombres négatifs car ce -n'est pas un chiffre.
Oli
12

Vous pouvez facilement le faire en utilisant Regex

>>> import re
>>> re.sub("\D","","£70,000")
70000
Aminah Nuraini
la source
De loin le moyen le plus simple
Iorek
5
En quoi est-ce différent de la réponse de João Silva, qui a été fournie 7 ans plus tôt?
jww
7
x.translate(None, string.digits)

supprimera tous les chiffres de la chaîne. Pour supprimer des lettres et conserver les chiffres, procédez comme suit:

x.translate(None, string.letters)
Terje Molnes
la source
3
J'obtiens un TypeError: translate () prend exactement un argument (2 donnés). Pourquoi cette question a été votée à la hausse dans son état actuel est assez frustrante.
Bobort
translate changé de python 2 à 3. La syntaxe utilisant cette méthode dans python 3 est x.translate (str.maketrans ('', '', string.digits)) et x.translate (str.maketrans ('', '' , string.ascii_letters)). Aucune de ces bandes n'espace blanc. Je ne recommanderais plus vraiment cette approche ...
ZaxR
5

L'op mentionne dans les commentaires qu'il souhaite conserver la décimale. Cela peut être fait avec la méthode re.sub (selon la seconde et la meilleure réponse à mon humble avis) en énumérant explicitement les caractères à conserver, par exemple

>>> re.sub("[^0123456789\.]","","poo123.4and5fish")
'123.45'
Roger Heathcote
la source
Et "poo123.4and.5fish"?
Jan Tojnar
Dans mon code, je vérifie le nombre de périodes dans la chaîne d'entrée et déclenche une erreur s'il est supérieur à 1.
Roger Heathcote
4

Une version rapide pour 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)

Voici une comparaison des performances par rapport à l'expression régulière:

$ 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

C'est donc un peu plus de 3 fois plus rapide que les regex, pour moi. C'est aussi plus rapide que class Delci-dessus, car defaultdictfait toutes ses recherches en C, plutôt qu'en Python (lent). Voici cette version sur mon même système, à titre de comparaison.

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

Utilisez une expression de générateur:

>>> s = "foo200bar"
>>> new_s = "".join(i for i in s if i in "0123456789")
bayer
la source
à la place''.join(n for n in foo if n.isdigit())
shxfee
2

Moche mais fonctionne:

>>> s
'aaa12333bb445bb54b5b52'
>>> a = ''.join(filter(lambda x : x.isdigit(), s))
>>> a
'1233344554552'
>>>
Gant
la source
pourquoi tu fais list(s)?
SilentGhost
@SilentGhost c'est mon malentendu. l'avait corrigé merci :)
Gant
En fait, avec cette méthode, je ne pense pas que vous ayez besoin d'utiliser "join". filter(lambda x: x.isdigit(), s)a bien fonctionné pour moi. ... oh, c'est parce que j'utilise Python 2.7.
Bobort
1
$ python -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'

100000 boucles, meilleur de 3: 2,48 usec par boucle

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

100000 boucles, meilleur de 3: 2,02 usec par boucle

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

100000 boucles, meilleur de 3: 2,37 usec par boucle

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

100000 boucles, meilleur de 3: 1,97 usec par boucle

J'avais observé que la jointure est plus rapide que les sous.

AnilReddy
la source
Pourquoi répétez-vous les deux méthodes deux fois? Et pourriez-vous décrire en quoi votre réponse est différente de celle acceptée?
Jan Tojnar
Les deux résultats le même résultat. Mais, je veux juste montrer que la jointure est plus rapide que la sous-méthode dans les résultats.
AnilReddy
Ils ne le font pas, votre code fait le contraire. Et vous avez aussi quatre mesures mais seulement deux méthodes.
Jan Tojnar
1

Vous pouvez lire chaque caractère. S'il s'agit d'un chiffre, incluez-le dans la réponse. La str.isdigit() méthode est un moyen de savoir si un caractère est un chiffre.

your_input = '12kjkh2nnk34l34'
your_output = ''.join(c for c in your_input if c.isdigit())
print(your_output) # '1223434'
Yoelvis
la source
en quoi est-ce différent de la réponse de f0b0s? Vous devriez plutôt modifier cette réponse si vous avez plus d'informations à apporter
chevybow
0

Pas une seule ligne mais très simple:

buffer = ""
some_str = "aas30dsa20"

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

print( buffer )
Josh
la source
0

J'ai utilisé ça. 'letters'doit contenir toutes les lettres dont vous souhaitez vous débarrasser:

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

Exemple:

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

Production: 20

Gustav
la source