2011-06-16 21 views
5

Tengo algunas líneas que representan algunos datos en un archivo de texto. Todos ellos son de la siguiente formato:Python Regex para hacer coincidir una cadena como un patrón y devolver el número

s = 'TheBears  SUCCESS Number of wins : 14' 

Todos comienzan con el nombre y luego el espacio en blanco y el texto 'ÉXITO número de victorias:' y, finalmente, el número de victorias, n1. Hay múltiples cadenas, cada una con un nombre y valor diferente. Estoy intentando escribir un programa que pueda analizar cualquiera de estas cadenas y devolver el nombre del conjunto de datos y el valor numérico al final de la cadena. Estoy tratando de utilizar expresiones regulares para hacer esto y yo he llegado con lo siguiente:

import re 
def winnumbers(s): 
    pattern = re.compile(r"""(?P<name>.*?)  #starting name 
          \s*SUCCESS  #whitespace and success 
          \s*Number\s*of\s*wins #whitespace and strings 
          \s*\:\s*(?P<n1>.*?)""",re.VERBOSE) 
    match = pattern.match(s) 

    name = match.group("name") 
    n1 = match.group("n1") 

    return (name, n1) 

Hasta ahora, mi programa puede devolver el nombre, pero el problema viene después de eso. Todos ellos tienen el texto "ÉXITO Número de victorias:" así que pensé en encontrar la manera de unir este texto. Pero me doy cuenta de que mi método de emparejar una subcadena exacta no es correcto en este momento. ¿Hay alguna forma de unir una subcadena completa como parte del patrón? Últimamente he estado leyendo bastante sobre expresiones regulares, pero no he encontrado nada como esto. Todavía soy nuevo en la programación y agradezco cualquier ayuda.

Eventualmente, usaré float() para devolver n1 como un número, pero lo dejé porque no encuentra el número en primer lugar en este momento y solo devolverá un error.

+2

Su problema es el uso del '. *?' Al final de la expresión regular. El '?' Lo hace perezoso lo que significa que coincidirá con la menor cantidad de caracteres posible, por lo que si finaliza una expresión regular con '. *?' No coincidirá con ningún carácter. O elimine '?' Para su grupo 'n1' o agregue un' $ 'al final de la expresión regular para que se forzará que coincida con el final de la línea. –

+0

¿Eliminar el? al final del grupo n1 hizo el truco! Muchas gracias. Tendré que tener eso en cuenta y ser más cuidadoso al respecto a partir de ahora. –

Respuesta

2

probar este:

((\S+)\s+SUCCESS Number of wins : (\d+)) 

Estos son los resultados:

>>> regex = re.compile("((\S+)\s+SUCCESS Number of wins : (\d+))") 
>>> r = regex.search(string) 
>>> r 
<_sre.SRE_Match object at 0xc827cf478a56b350> 
>>> regex.match(string) 
<_sre.SRE_Match object at 0xc827cf478a56b228> 

# List the groups found 
>>> r.groups() 
(u'TheBears SUCCESS Number of wins : 14', u'TheBears', u'14') 

# List the named dictionary objects found 
>>> r.groupdict() 
{} 

# Run findall 
>>> regex.findall(string) 
[(u'TheBears SUCCESS Number of wins : 14', u'TheBears', u'14')] 
# So you can do this for the name and number: 
>>> fullstring, name, number = r.groups() 

Si usted no necesita la cadena completa Basta con retirar el paréntesis envolvente.

2

Creo que no hay una necesidad real de usar una expresión regular aquí. Por lo tanto se puede utilizar el siguiente código si es aceptable para usted (en cuenta que he publicado es por lo que tendrá capacidad para tener otra opción):

dict((line[:line.lower().index('success')+1], line[line.lower().index('wins:') + 6:]) for line in text.split('\n') if 'success' in line.lower()) 

o en caso de que esté seguro de que todas las palabras de todas formas separado por espacios simples:

output={} 
for line in text: 
    if 'success' in line.lower(): 
     words = line.strip().split(' ') 
     output[words[0]] = words[-1] 
+2

+1 para el one-liner casi ilegible;) – fijter

+0

Se agregó una solución legible)) –

1

Si el texto en el medio es siempre constante, no hay necesidad de una expresión regular. Las funciones de procesamiento de cadenas incorporadas serán más eficientes y fáciles de desarrollar, depurar y mantener. En este caso, sólo puede utilizar la función incorporada split() para conseguir las piezas, y luego limpiar las dos piezas según el caso:

>>> def winnumber(s): 
...  parts = s.split('SUCCESS Number of wins : ') 
...  return (parts[0].strip(), int(parts[1])) 
... 
>>> winnumber('TheBears  SUCCESS Number of wins : 14') 
('TheBears', 14) 

en cuenta que he de salida el número de victorias como un entero (como presumiblemente esta voluntad siempre será un número entero), pero puede sustituir fácilmente float() - o cualquier otra función de conversión - por int() si lo desea.

Editar: Obviamente, esto solo funcionará para líneas individuales: si llama a la función con varias líneas, le dará errores.Para procesar un archivo completo, que haría uso de map():

>>> map(winnumber, open(filename, 'r')) 
[('TheBears', 14), ('OtherTeam', 6)] 

Además, no estoy seguro de su uso final de este código, pero puede que le resulte más fácil trabajar con las salidas como un diccionario:

>>> dict(map(winnumber, open(filename, 'r'))) 
{'OtherTeam': 6, 'TheBears': 14} 
Cuestiones relacionadas