web-dev-qa-db-fra.com

Comment extraire un nombre flottant d'une chaîne

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

84
Flowpoke

Si votre float est toujours exprimé 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 souhaitez valider la saisie de l'utilisateur, vous pouvez également rechercher un flottant en y accédant 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
165
miku

Vous voudrez peut-être essayer quelque chose comme ceci qui couvre toutes les bases, y compris ne pas compter sur les espaces après le nombre:

>>> 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 un copier-coller facile:

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")
54
John Machin

Python docs a une réponse qui couvre la notation +/- et l’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 dans lesquels une virgule est utilisée comme séparateur entre la partie entière et la partie décimale (3,14159). Dans ce cas, remplacez tous les \. avec [.,] dans la regex float ci-dessus.

                        Regular Expression
International float     [-+]?(\d+([.,]\d*)?|[.,]\d+)([eE][-+]?\d+)?
22
IceArdor
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")

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

6
Martin

Vous pouvez utiliser l'expression régulière suivante pour obtenir des valeurs entières et flottantes à partir 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

4
user3613331

Je pense que vous trouverez des choses intéressantes dans la réponse suivante que j'ai faite pour une question similaire précédente:

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

Dans cette réponse, j'ai proposé un modèle qui permet à un regex d'attraper n'importe quel nombre et comme je n'ai rien d'autre à ajouter, je pense qu'il est assez complet

2
eyquem

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

>>> 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 a aussi des inconvénients cependant. Si quelqu'un tape "1 000", cela sera converti en 1. En outre, cela suppose que les gens entrent avec des espaces entre les mots. Ce n'est pas le cas avec d'autres langues, telles que le chinois.

0
Tim McNamara