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]
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.
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]
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
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
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
# 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]
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'
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)
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)
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.
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]
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
@ 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)
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)