2011-01-16 7 views
57

J'ai un certain nombre de chaînes similaires à Current Level: 13.4 db. et je voudrais extraire juste le nombre à virgule flottante. Je dis flottant et non décimal car c'est parfois entier. RegEx peut-il faire cela ou existe-t-il un meilleur moyen?Comment extraire un nombre flottant d'une chaîne

+0

ce qu'il y aura toujours une partie entière? Même si c'est 0? Avez-vous besoin de faire correspondre 0,4 ou 0,4? – Falmarri

+0

Je dirais oui. L'entrée est entrée manuellement afin d'éviter toute incohérence. – Flowpoke

Répondre

112

Si votre flotteur est toujours exprimée en notation décimale quelque chose comme

>>> import re 
>>> re.findall("\d+\.\d+", "Current Level: 13.4 db.") 
['13.4'] 

peut suffire.

Une version plus robuste serait:

>>> re.findall(r"[-+]?\d*\.\d+|\d+", "Current Level: -13.2 db or 14.2 or 3") 
['-13.2', '14.2', '3'] 

Si vous voulez valider l'entrée d'utilisateur, vous pourriez aussi vérifier aussi pour un flotteur en marchant à directement:

user_input = "Current Level: 1e100 db" 
for token in user_input.split(): 
    try: 
     # if this succeeds, you have your (first) float 
     print float(token), "is a float" 
    except ValueError: 
     print token, "is something else" 

# => Would print ... 
# 
# Current is something else 
# Level: is something else 
# 1e+100 is a float 
# db is something else 
+0

ce n'est pas toujours un nombre décimal. – Flowpoke

+2

're.findall (r" [- +]? \ D * \. * \ D + "," Niveau actuel: -13.2 db ou 14.2 ou 3 ")' '['-13.2', '14 .2 ',' 3 '] ' – JuanPablo

+1

Je pense que vous vouliez dire" \ d + \. \ D + "au lieu de" \ d +. \ D + "dans votre premier bloc de code. En ce moment, il faudrait extraire quelque chose comme "13a4". – abw333

0

Une autre approche peut être plus lisible est la conversion de type simple. J'ai ajouté une fonction de remplacement pour couvrir les cas où les gens peuvent entrer des nombres décimaux européens:

>>> for possibility in "Current Level: -13.2 db or 14,2 or 3".split(): 
...  try: 
...   str(float(possibility.replace(',', '.'))) 
...  except ValueError: 
...   pass 
'-13.2' 
'14.2' 
'3.0' 

Cela présente cependant des inconvénients. Si quelqu'un tape "1,000", cela sera converti en 1. De plus, il suppose que les gens entreront avec des espaces entre les mots. Ce n'est pas le cas avec d'autres langues, comme le chinois.

+0

"4 piles AAA 1.5V incluses": -) –

+0

Ces terribles utilisateurs! Toujours entrer dans des données stupides. TBH, j'ai volontairement gardé cet exemple démonstratif plutôt que robuste. Quand j'ai commencé à écrire cette réponse, @ MYYN n'a fourni que des expressions régulières dans la réponse acceptée. Je voulais donner un exemple d'une autre façon de faire les choses. –

37

Vous pouvez vous essayer quelque chose comme ce qui couvre toutes les bases, y compris ne pas compter sur les espaces après le numéro:

>>> import re 
>>> numeric_const_pattern = r""" 
...  [-+]? # optional sign 
...  (?: 
...   (?: \d* \. \d+) # .1 .12 .123 etc 9.1 etc 98.1 etc 
...   | 
...   (?: \d+ \.?) # 1. 12. 123. etc 1 12 123 etc 
... ) 
...  # followed by optional exponent part if desired 
...  (?: [Ee] [+-]? \d+) ? 
...  """ 
>>> rx = re.compile(numeric_const_pattern, re.VERBOSE) 
>>> rx.findall(".1 .12 9.1 98.1 1. 12. 1 12") 
['.1', '.12', '9.1', '98.1', '1.', '12.', '1', '12'] 
>>> rx.findall("-1 +1 2e9 +2E+09 -2e-9") 
['-1', '+1', '2e9', '+2E+09', '-2e-9'] 
>>> rx.findall("current level: -2.03e+99db") 
['-2.03e+99'] 
>>> 

Pour faciliter le copier-coller:

numeric_const_pattern = '[-+]? (?: (?: \d* \. \d+) | (?: \d+ \.?))(?: [Ee] [+-]? \d+) ?' 
rx = re.compile(numeric_const_pattern, re.VERBOSE) 
rx.findall("Some example: Jr. it. was .23 between 2.3 and 42.31 seconds") 
+2

Très bien! Finalement, j'ai trouvé un très bon motif! –

+0

Oui, le meilleur motif pour les nombres. Merci beaucoup! – edisonex

+0

Ajout de '(?: \ + \ S * | \ - \ s *)?' À l'avant permettrait également un espace entre le signe et le nombre. Même si j'admets que ce n'est probablement pas très "standard", j'ai vu ce motif "flotter" dans certains fichiers. – NOhs

5
re.findall(r"[-+]?\d*\.\d+|\d+", "Current Level: -13.2 db or 14.2 or 3") 

comme décrit ci-dessus, fonctionne vraiment bien! Une suggestion cependant:

re.findall(r"[-+]?\d*\.\d+|[-+]?\d+", "Current Level: -13.2 db or 14.2 or 3 or -3") 

retourne également les valeurs int négatives (comme -3 à la fin de cette chaîne)

2

Je pense que vous trouverez des choses intéressantes dans la réponse suivante à moi que je a fait une précédente question similaire:

https://stackoverflow.com/q/5929469/551449

Dans cette réponse, j'ai proposé un modèle qui permet une expression régulière pour attraper tout type de nombre et que je n'ai rien d'autre à ajouter, je pense il est assez complet

13

Python docs a une réponse qui couvre +/-, et la notation exposant

scanf() Token  Regular Expression 
%e, %E, %f, %g  [-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)? 
%i     [-+]?(0[xX][\dA-Fa-f]+|0[0-7]*|\d+) 

Cette expression régulière ne prend pas en charge les formats internationaux où une virgule est utilisé comme séparateur entre l'ensemble et fractionnelle partie (3,14159). Dans ce cas, remplacez tous \. par [.,] dans l'expression rationnelle flottante ci-dessus.

     Regular Expression 
International float  [-+]?(\d+([.,]\d*)?|[.,]\d+)([eE][-+]?\d+)? 
2

Vous pouvez utiliser l'expression rationnelle suivante pour obtenir des valeurs entières et flottantes d'une chaîne:

re.findall(r'[\d\.\d]+', 'hello -34 42 +34.478m 88 cricket -44.3') 

['34', '42', '34.478', '88', '44.3'] 

Merci Rex

+2

Cette regex trouvera également des combinaisons non numériques de points et de chiffres: ''.... 1.2.3.4 ..56 ..'' donne: '['....', '1.2.3.4', '. .56 .. '] ' – scottbb

Questions connexes