python3: extraer la dirección IP del patrón compilado

8

Quiero procesar cada línea en mi archivo de registro y extraer la IPdirección si la línea coincide con mi patrón. Hay varios tipos diferentes de mensajes, en el ejemplo a continuación estoy usando p1 andp2`.

Podía leer el archivo línea por línea, y para cada línea coincidir con cada patrón. Pero como puede haber muchos más patrones, me gustaría hacerlo de la manera más eficiente posible. Esperaba compilar estos patrones en un objeto, y hacer la coincidencia solo una vez para cada línea:

import re

IP = r'(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'

p1 = 'Registration from' + IP + '- Wrong password' 
p2 = 'Call from' + IP + 'rejected because extension not found'

c = re.compile(r'(?:' + p1 + '|' + p2 + ')')

for line in sys.stdin:
    match = re.search(c, line)
    if match:
        print(match['ip'])

pero el código anterior no funciona, se queja de que ipse usa dos veces.

¿Cuál es la forma más elegante de lograr mi objetivo?

EDITAR:

He modificado mi código basado en la respuesta de @Dev Khadka.

Pero todavía estoy luchando sobre cómo manejar adecuadamente las múltiples ipcoincidencias. El siguiente código imprime todas las IP que coinciden con p1:

for line in sys.stdin:
    match = c.search(line)
    if match:
        print(match['ip1'])

Pero algunas líneas no coinciden p1. Ellos coinciden p2. es decir, obtengo:

1.2.3.4
None
2.3.4.5
...

¿Cómo se imprime la IP correspondiente, cuando no sé que era wheter p1, p2, ...? Todo lo que quiero es la IP. No me importa qué patrón coincida.

Martin Vegter
fuente
1
Debe proporcionar sus datos de prueba.
eyllanesc

Respuestas:

4

Puede considerar instalar el excelente regexmódulo, que admite muchas características avanzadas de expresiones regulares, incluidos los grupos de restablecimiento de rama , diseñados para resolver exactamente el problema que describió en esta pregunta. Los grupos de restablecimiento de rama se denotan por (?|...). Todos los grupos de captura de las mismas posiciones o nombres en diferentes patrones alternativos dentro de un grupo de restablecimiento de rama comparten los mismos grupos de captura para la salida.

Observe que en el ejemplo a continuación, el grupo de captura coincidente se convierte en el grupo de captura con nombre, por lo que no necesita iterar sobre múltiples grupos buscando un grupo no vacío:

import regex

ip_pattern = r'(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
patterns = [
    'Registration from {ip} - Wrong password',
    'Call from {ip} rejected because extension not found'
]
pattern = regex.compile('(?|%s)' % '|'.join(patterns).format(ip=ip_pattern))
for line in sys.stdin:
    match = regex.search(pattern, line)
    if match:
        print(match['ip'])

Demostración: https://repl.it/@blhsing/RegularEmbellishedBugs

blhsing
fuente
1
¡Esto es perfecto! Gracias.
Martin Vegter
2

¿Por qué no compruebas qué expresión regular coincide?

if 'ip1' in match :
    print match['ip1']
if 'ip2' in match :
    print match['ip2']

o algo como:

names = [ 'ip1', 'ip2', 'ip3' ]
for n in names :
    if n in match :
        print match[n]

o incluso

num = 1000   # can easily handle millions of patterns =)
for i in range(num) :
    name = 'ip%d' % i
    if name in match :
        print match[name]
lenik
fuente
pero que pasa si tengo 100 patrones? ¿Puedo hacer esto en un bucle? ¿Puedo iterar sobre el match[i]bucle for?
Martin Vegter
@MartinVegter ver arriba
lenik
@MartinVegter puede manejar millones de patrones fácilmente =)
lenik
Me sale un error:if match[name] is not None: IndexError: no such group
Martin Vegter
@MartinVegter intenta usar name in matchen su lugar
lenik
1

eso es porque estás usando el mismo nombre de grupo para dos grupos

intente esto, esto le dará los nombres de grupo ip1 e ip2

import re

IP = r'(?P<ip%d>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'

p1 = 'Registration from' + IP%1 + '- Wrong password' 
p2 = 'Call from' + IP%2 + 'rejected because extension not found'

c = re.compile(r'(?:' + p1 + '|' + p2 + ')')
Dev Khadka
fuente
1

Los grupos de captura con nombre deben tener nombres distintos, pero dado que todos sus grupos de captura están destinados a capturar el mismo patrón, es mejor no usar grupos de captura con nombre en este caso, sino simplemente usar grupos de captura regulares e iterar a través de los grupos desde el objeto coincidente para imprimir el primer grupo que no está vacío:

ip_pattern = r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
patterns = [
    'Registration from {ip} - Wrong password',
    'Call from {ip} rejected because extension not found'
]
pattern = re.compile('|'.join(patterns).format(ip=ip_pattern))
for line in sys.stdin:
    match = re.search(pattern, line)
    if match:
        print(next(filter(None, match.groups())))

Demostración: https://repl.it/@blhsing/UnevenCheerfulLight

blhsing
fuente