¿Cómo puedo extraer una expresión numérica de un string, que puede tener o no underscore o hyphen? Por ej. como 2016-03 o 2016_03 o simplemente 201603.

Cadenas de muestra:

s = 'Total revenue for 2016-03 is 3000 €'  # Output 2016-03
s = 'Total revenue for 2016_03 is 3000 €'  # Output 2016_03
s = 'Total revenue for 201603 is 3000 €'   # Output 201603

Hay 6 números y en caso de que tengamos - o _, entonces la longitud total es 7. No hay otros number en toda la cadena.

No sé cómo usar if-else en regex, por lo que puede incluir la lógica de longitud 6 o 7. Para cadenas simples como 201603, puedo hacerlo:

import re
print(re.findall('\d{6}','Total revenue for 201603 is 3000 €'))
['201603']

print(re.findall('\d{6}','Total revenue for 2016-03 is 3000 €'))
[]

Nota: estoy buscando una solución en la que teóricamente _ o - podrían estar entre el número de 6 longitudes. Como 123-456 o 123456 o 12345-6 y así sucesivamente.

3
cph_sto 7 oct. 2019 a las 15:38

4 respuestas

La mejor respuesta

Puede haber dos enfoques: uno es más legible al dividir la cadena primero y luego obtener el primer elemento que coincida con su patrón requerido, o un enfoque menos legible con una sola expresión regular.

Consulte la demostración de Python:

import re
s = 'Total revenue for 201603 is 3000 €'
rx = re.compile(r'^(?=\d+(?:[_-]\d+)?$)[\d_-]{6,7}$')
res = [x for x in s.split() if rx.search(x)]
if len(res):
    print(res[0])

# Pure regex approach:
rx = re.compile(r'(?<!\S)(?=\d+(?:[_-]\d+)?(?!\S))[\d_-]{6,7}(?!\S)')
res = rx.search(s)
if res:
    print(res.group())

Entonces, en el primer enfoque, la cadena se divide con espacios en blanco, y se aplica un patrón ^(?=\d+(?:[_-]\d+)?$)[\d_-]{6,7}$ a cada elemento, y si hay coincidencias, se devuelve el primero. El patrón coincide:

  • ^ - inicio de cadena
  • (?=\d+(?:[_-]\d+)?$): una anticipación positiva que asegura que haya 1+ dígitos, luego _ o -, y luego de nuevo 1+ dígitos hasta el final de la cadena,
  • [\d_-]{6,7}: coincide con 6 o 7 dígitos, - o _
  • $ - fin de la cadena.

El segundo enfoque involucra solo expresiones regulares y el anclaje ^ se responde con (?<!\S) y $ se reemplaza con (?!\S) que actúan como límites de espacios en blanco. (?<!\S) es una mirada hacia atrás negativa que requiere un espacio en blanco o el inicio de la cadena justo antes de la posición actual y (?!\S) es una búsqueda negativa hacia adelante que requiere un espacio en blanco o el final de la cadena justo después de la posición actual.

2
Wiktor Stribiżew 7 oct. 2019 a las 13:18

Esto debería hacerlo de manera bastante simple:

print(re.findall(r'\d{4}[-_]?\d{2}', 'Total revenue for 201603 is 3000 €'))
# ['201603']

Específicamente, esto es "Cuatro dígitos, seguidos de cero o una aparición de '-' o '_', seguido de dos dígitos más". Si no hay un guión o guión bajo, los cuatro dígitos y los dos dígitos terminan igual que pedir seis dígitos.

Sin embargo, este captura el guión o el guión bajo si está allí, por lo que una cosa que puede hacer es filtrarlo:

nums = re.findall(r'\d{4}[-_]?\d{2}', 'Total revenue for 2016-03 is 3000 €')
# nums = ['2016-03']
nums = [num.replace('-', '').replace('_', '') for num in nums]
# nums = ['201603']

Tenga en cuenta que esta es la solución que menos perturba su expresión regular original, y buscará este patrón de "cuatro dígitos seguido de quizás un separador y luego dos dígitos" en cualquier parte de la cadena. Si desea restringir esto a solo la cadena que está tratando de buscar, ignorando las similares, es posible que deba hacer que la expresión regular sea más específica. Consulte también la documentación re

0
Green Cloak Guy 7 oct. 2019 a las 12:44

Su RegEx es el siguiente: comienza con espacio, secuencia de al menos un dígito (s) y termina con espacio. Se trata de esto:

\s(\d*)\s

Compruébalo aquí: https://regex101.com/r/V4NzLj/1

0
tgralex 7 oct. 2019 a las 12:59

Puede usar una mirada hacia atrás positiva si está seguro de que su valor requerido siempre sigue un patrón estándar

(?<=^Total revenue for )\d+[-_]?\d+
  • (?<=^Total revenue for ) - La coincidencia debe estar precedida por Total revenue for, ^ comenzar desde el inicio de la cadena
  • \d+: coincide con uno o más dígitos
  • [-_]? - Match - or _ (opcional)

Regex Demo


O puede extender la expresión regular anterior de tal manera si no está seguro de que el formato de valor requerido

(?<=^Total revenue for )(?=\d+[-_]?\d+)[\d_-]{6,7}(?!\S)
  • (?=\d+[-_]?\d+) - Para garantizar digit followed by - or _ optional followed by digit
  • [\d_-]{6,7} - Para que coincida con digit or - or _, 6 or 7 times
  • (?!\S): no debe ir seguido de un carácter que no sea espacio

Regex Demo

1
Code Maniac 7 oct. 2019 a las 13:14
58269727