web-dev-qa-db-fra.com

Comment extraire des nombres d'une chaîne en Python?

Je voudrais extraire tous les nombres contenus dans une chaîne. Laquelle est la mieux adaptée à l’objet, aux expressions régulières ou à la méthode isdigit()?

Exemple:

line = "hello 12 hi 89"

Résultat:

[12, 89]
365
pablouche

Si vous souhaitez uniquement extraire uniquement les entiers positifs, essayez ce qui suit:

>>> str = "h3110 23 cat 444.4 rabbit 11 2 dog"
>>> [int(s) for s in str.split() if s.isdigit()]
[23, 11, 2]

Je dirais que c'est mieux que l'exemple de regex pour trois raisons. D'abord, vous n'avez pas besoin d'un autre module. Deuxièmement, il est plus lisible, car vous n'avez pas besoin d'analyser le mini-langage de la regex. et troisièmement, il est plus rapide (et donc probablement plus pythonique):

python -m timeit -s "str = 'h3110 23 cat 444.4 rabbit 11 2 dog' * 1000" "[s for s in str.split() if s.isdigit()]"
100 loops, best of 3: 2.84 msec per loop

python -m timeit -s "import re" "str = 'h3110 23 cat 444.4 rabbit 11 2 dog' * 1000" "re.findall('\\b\\d+\\b', str)"
100 loops, best of 3: 5.66 msec per loop

Cela ne reconnaîtra pas les flottants, les entiers négatifs ou entiers au format hexadécimal. Si vous ne pouvez pas accepter ces limitations, réponse de slim ci-dessous fera l'affaire.

412
fmark

J'utiliserais une expression rationnelle:

>>> import re
>>> re.findall(r'\d+', 'hello 42 I\'m a 32 string 30')
['42', '32', '30']

Cela correspondrait également à 42 de bla42bla. Si vous souhaitez uniquement des nombres délimités par des limites de Word (espace, point, virgule), vous pouvez utiliser\b:

>>> re.findall(r'\b\d+\b', 'he33llo 42 I\'m a 32 string 30')
['42', '32', '30']

Pour vous retrouver avec une liste de nombres au lieu d'une liste de chaînes:

>>> [int(s) for s in re.findall(r'\b\d+\b', 'he33llo 42 I\'m a 32 string 30')]
[42, 32, 30]
382
Vincent Savard

Ceci est plus qu’un peu en retard, mais vous pouvez également élargir l’expression rationnelle pour prendre en compte la notation scientifique.

import re

# Format is [(<string>, <expected output>), ...]
ss = [("Apple-12.34 ba33na fanc-14.23e-2yapple+45e5+67.56E+3",
       ['-12.34', '33', '-14.23e-2', '+45e5', '+67.56E+3']),
      ('hello X42 I\'m a Y-32.35 string Z30',
       ['42', '-32.35', '30']),
      ('he33llo 42 I\'m a 32 string -30', 
       ['33', '42', '32', '-30']),
      ('h3110 23 cat 444.4 rabbit 11 2 dog', 
       ['3110', '23', '444.4', '11', '2']),
      ('hello 12 hi 89', 
       ['12', '89']),
      ('4', 
       ['4']),
      ('I like 74,600 commas not,500', 
       ['74,600', '500']),
      ('I like bad math 1+2=.001', 
       ['1', '+2', '.001'])]

for s, r in ss:
    rr = re.findall("[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", s)
    if rr == r:
        print('GOOD')
    else:
        print('WRONG', rr, 'should be', r)

Donne tout bon!

De plus, vous pouvez consulter le regex intégré AWS Glue

80

Je suppose que vous voulez des flottants et pas seulement des nombres entiers, je ferais donc quelque chose comme ceci:

l = []
for t in s.split():
    try:
        l.append(float(t))
    except ValueError:
        pass

Notez que certaines des autres solutions publiées ici ne fonctionnent pas avec des nombres négatifs:

>>> re.findall(r'\b\d+\b', 'he33llo 42 I\'m a 32 string -30')
['42', '32', '30']

>>> '-3'.isdigit()
False
64
jmnas

Si vous savez qu'il n'y aura qu'un seul nombre dans la chaîne, c'est-à-dire "bonjour 12 salut", vous pouvez essayer le filtre.

Par exemple:

In [1]: int(''.join(filter(str.isdigit, '200 grams')))
Out[1]: 200
In [2]: int(''.join(filter(str.isdigit, 'Counters: 55')))
Out[2]: 55
In [3]: int(''.join(filter(str.isdigit, 'more than 23 times')))
Out[3]: 23

Mais soyez prudent !!! :

In [4]: int(''.join(filter(str.isdigit, '200 grams 5')))
Out[4]: 2005
53
dfostic
# extract numbers from garbage string:
s = '12//n,_@#$%3.14kjlw0xdadfackvj1.6e-19&*ghn334'
newstr = ''.join((ch if ch in '0123456789.-e' else ' ') for ch in s)
listOfNumbers = [float(i) for i in newstr.split()]
print(listOfNumbers)
[12.0, 3.14, 0.0, 1.6e-19, 334.0]
12
AndreiS

Je cherchais une solution pour supprimer les masques de chaînes, en particulier des numéros de téléphone brésiliens. Ce message ne m'a pas répondu, mais m'a inspiré. Ceci est ma solution:

>>> phone_number = '+55(11)8715-9877'
>>> ''.join([n for n in phone_number if n.isdigit()])
'551187159877'
9
Sidon

Cette réponse contient également le cas où le nombre est flottant dans la chaîne

def get_first_nbr_from_str(input_str):
    '''
    :param input_str: strings that contains digit and words
    :return: the number extracted from the input_str
    demo:
    'ab324.23.123xyz': 324.23
    '.5abc44': 0.5
    '''
    if not input_str and not isinstance(input_str, str):
        return 0
    out_number = ''
    for ele in input_str:
        if (ele == '.' and '.' not in out_number) or ele.isdigit():
            out_number += ele
        Elif out_number:
            break
    return float(out_number)
7
Menglong Li

Utiliser Regex ci-dessous est le chemin

lines = "hello 12 hi 89"
import re
output = []
line = lines.split()
for Word in line:
        match = re.search(r'\d+.?\d*', Word)
        if match:
            output.append(float(match.group()))
print (output)
6
sim

Je suis surpris de voir que personne n’a encore mentionné l’utilisation de itertools.groupby comme alternative pour y parvenir. .

Vous pouvez utiliser itertools.groupby() avec str.isdigit() afin d'extraire des nombres de chaîne comme:

_from itertools import groupby
my_str = "hello 12 hi 89"

l = [int(''.join(i)) for is_digit, i in groupby(my_str, str.isdigit) if is_digit]
_

La valeur retenue par l sera:

_[12, 89]
_

PS: Ceci est juste à titre d'illustration pour montrer que Comme alternative, nous pourrions aussi utiliser groupby pour y parvenir. Mais ce n'est pas une solution recommandée. Si vous voulez y parvenir, vous devriez utiliser réponse acceptée de fmark sur la base de l'utilisation de la compréhension de liste avec _str.isdigit_ comme filtre.

5
Moinuddin Quadri

J'ajoute simplement cette réponse parce que personne n'en a ajouté une en utilisant la gestion des exceptions et parce que cela fonctionne aussi pour les floats

a = []
line = "abcd 1234 efgh 56.78 ij"
for Word in line.split():
    try:
        a.append(float(Word))
    except ValueError:
        pass
print(a)

Sortie:

[1234.0, 56.78]
2
Raghav

Puisqu'aucune de celles-ci ne traitait des chiffres financiers réels que je devais trouver dans les documents Excel et Word, voici ma variante. Il gère les ints, les flottants, les nombres négatifs, les numéros de devise (car il ne répond pas lors de la scission), et a la possibilité de supprimer la partie décimale et de ne renvoyer que des ints ou tout.

Il gère également le système de numération Indian Laks où les virgules apparaissent irrégulièrement, pas tous les 3 chiffres à part.

Il ne gère pas la notation scientifique ou les nombres négatifs placés entre parenthèses dans les budgets - apparaîtra positif.

De plus, il n’extrait pas de dates. Il existe de meilleurs moyens de rechercher des dates dans des chaînes.

import re
def find_numbers(string, ints=True):            
    numexp = re.compile(r'[-]?\d[\d,]*[\.]?[\d{2}]*') #optional - in front
    numbers = numexp.findall(string)    
    numbers = [x.replace(',','') for x in numbers]
    if ints is True:
        return [int(x.replace(',','').split('.')[0]) for x in numbers]            
    else:
        return numbers
2
Marc Maxmeister

@ Jmnas, j'ai bien aimé votre réponse, mais elle n'a pas trouvé de flotteurs. Je travaille sur un script pour analyser le code envoyé à une usine de traitement numérique et je devais trouver les dimensions X et Y pouvant être des entiers ou des flottants. J'ai donc adapté votre code à ce qui suit. Cela trouve int, float avec des valeurs positives et négatives. Vous ne trouvez toujours pas les valeurs au format hexadécimal, mais vous pouvez ajouter "x" et "A" à "F" dans le tuple num_char et je pense que cela analyserait des éléments comme "0x23AC".

s = 'hello X42 I\'m a Y-32.35 string Z30'
xy = ("X", "Y")
num_char = (".", "+", "-")

l = []

tokens = s.split()
for token in tokens:

    if token.startswith(xy):
        num = ""
        for char in token:
            # print(char)
            if char.isdigit() or (char in num_char):
                num = num + char

        try:
            l.append(float(num))
        except ValueError:
            pass

print(l)
1
ZacSketches

La meilleure option que j'ai trouvée est ci-dessous. Il va extraire un nombre et peut éliminer tout type de caractère.

def extract_nbr(input_str):
    if input_str is None or input_str == '':
        return 0

    out_number = ''
    for ele in input_str:
        if ele.isdigit():
            out_number += ele
    return float(out_number)    
0
Ajay Kumar