Python: Extraer números de una cadena

Yo extraería todos los números contenidos en una cadena. ¿Cuál es el más adecuado para el propósito, las expresiones regulares o el método isdigit() ?

Ejemplo:

 line = "hello 12 hi 89" 

Resultado:

 [12, 89] 

Si solo desea extraer solo enteros positivos, intente lo siguiente:

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

Yo diría que esto es mejor que el ejemplo de expresiones regulares por tres razones. Primero, no necesitas otro módulo; en segundo lugar, es más legible porque no necesita analizar el mini-lenguaje de expresiones regulares; y tercero, es más rápido (y, por lo tanto, probablemente más python):

 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 

Esto no reconocerá flotantes, enteros negativos o enteros en formato hexadecimal. Si no puedes aceptar estas limitaciones, la respuesta de Slim a continuación hará el truco.

Yo usaría una expresión regular:

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

Esto también coincidiría con 42 de bla42bla . Si solo quieres números delimitados por límites de palabras (espacio, punto, coma), puedes usar \ b:

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

Para terminar con una lista de números en lugar de una lista de cadenas:

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

Esto es más que un poco tarde, pero también puede extender la expresión regular para tener en cuenta la notación científica.

 import re # Format is [(, ), ...] 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\'ma Y-32.35 string Z30', ['42', '-32.35', '30']), ('he33llo 42 I\'ma 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) 

¡Da todo lo bueno!

Además, puede consultar la expresión regular incorporada de AWS Glue

Asumo que quieres flotadores no solo enteros, así que haría algo como esto:

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

Tenga en cuenta que algunas de las otras soluciones publicadas aquí no funcionan con números negativos:

 >>> re.findall(r'\b\d+\b', 'he33llo 42 I\'ma 32 string -30') ['42', '32', '30'] >>> '-3'.isdigit() False 

Si sabe que solo habrá un número en la cadena, es decir, ‘hola 12 hola’, puede probar el filtro.

Por ejemplo:

 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 

¡Pero ten cuidado! :

 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] 

Esta respuesta también contiene el caso cuando el número es flotante en la cadena

 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) 

Estaba buscando una solución para eliminar las máscaras de cuerdas, específicamente de los números de teléfonos brasileños, esta publicación no fue respondida pero me inspiró. Esta es mi solución:

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

Me sorprende ver que nadie ha mencionado aún el uso de itertools.groupby como alternativa para lograr esto.

Puede usar itertools.groupby() junto con str.isdigit() para extraer números de la cadena como:

 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] 

El valor retenido por l será:

 [12, 89] 

PD: Esto es solo con fines ilustrativos para mostrar que, como alternativa, también podríamos usar groupby para lograr esto. Pero esta no es una solución recomendada. Si desea lograr esto, debe utilizar la respuesta aceptada de fmark basándose en el uso de la comprensión de lista con str.isdigit como filtro.

Usar Regex a continuación es el camino

 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) 

Como ninguno de estos se ocupó de los números financieros del mundo real en Excel y documentos de Word que necesitaba encontrar, aquí está mi variación. Maneja ints, flotantes, números negativos, números de moneda (porque no responde en la división) y tiene la opción de descartar la parte decimal y devolver solo ints, o devolver todo.

También maneja el sistema de números de Indian Laks donde las comas aparecen de manera irregular, no cada 3 números separados.

No maneja la notación científica o los números negativos colocados dentro de paréntesis en los presupuestos, aparecerán positivos.

Tampoco extrae fechas. Hay mejores maneras de encontrar fechas en cadenas.

 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, me gustó tu respuesta, pero no encontró flotadores. Estoy trabajando en una secuencia de comandos para analizar el código que va a un molino CNC y necesitaba encontrar las dimensiones X e Y que pueden ser enteros o flotantes, por lo que adapté su código a lo siguiente. Esto encuentra int, flotar con valores positivos y negativos. Aún no encuentra valores con formato hexadecimal, pero podría agregar “x” y “A” a “F” a la tupla num_char y creo que analizaría cosas como ‘0x23AC’.

 s = 'hello X42 I\'ma 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 mejor opción que encontré está abajo. Extraerá un número y podrá eliminar cualquier tipo de char.

 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)