python3: extrai o endereço IP do padrão compilado

8

Quero processar todas as linhas do meu arquivo de log e extrair o IPendereço se a linha corresponder ao meu padrão. Existem vários tipos diferentes de mensagens, no exemplo abaixo, estou usando p1 andp2`.

Eu podia ler o arquivo linha por linha e para cada linha corresponder a cada padrão. Mas como pode haver muitos outros padrões, eu gostaria de fazê-lo da maneira mais eficiente possível. Eu esperava compilar esses padrões em um objeto e fazer a correspondência apenas uma vez para cada linha:

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'])

mas o código acima não funciona, ele reclama que ipé usado duas vezes.

Qual é a maneira mais elegante de alcançar meu objetivo?

EDITAR:

Modifiquei meu código com base nas respostas de @Dev Khadka.

Mas ainda estou lutando para lidar adequadamente com as várias ipcorrespondências. O código abaixo imprime todos os IPs correspondentes a p1:

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

Mas algumas linhas não correspondem p1. Eles combinamp2 . ou seja, eu recebo:

1.2.3.4
None
2.3.4.5
...

Como faço para imprimir o ip de correspondência, quando eu não sei wheter que era p1, p2...? Tudo o que eu quero é o IP. Eu não ligo para qual padrão ele combina.

Martin Vegter
fonte
1
Você deve fornecer seus dados de teste.
eyllanesc

Respostas:

4

Você pode considerar instalar o regexmódulo excelente , que suporta muitos recursos avançados de regex, incluindo grupos de redefinição de ramificação , projetados para resolver exatamente o problema descrito nesta pergunta. Os grupos de redefinição de ramificação são indicados por(?|...) . Todos os grupos de captura das mesmas posições ou nomes em diferentes padrões alternativos em um grupo de redefinição de ramificação compartilham os mesmos grupos de captura para saída.

Observe que, no exemplo abaixo, o grupo de captura correspondente se torna o grupo de captura nomeado, para que você não precise iterar sobre vários grupos procurando um grupo não vazio:

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'])

Demonstração: https://repl.it/@blhsing/RegularEmbellishedBugs

blhsing
fonte
1
Isto é perfeito! Obrigado.
Martin Vegter 21/10/19
2

por que você não verifica qual regex corresponde?

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

ou algo como:

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

ou mesmo

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
fonte
mas e se eu tiver 100 padrões? Posso fazer isso em um loop? Posso iterar match[i]no loop for?
Martin Vegter 21/10/19
@MartinVegter veja acima
lenik
@MartinVegter pode lidar com milhões de padrões facilmente =)
lenik
Eu recebo um erro:if match[name] is not None: IndexError: no such group
Martin Vegter 21/10/19
@MartinVegter tentar usar name in matchvez
lenik
1

isso é porque você está usando o mesmo nome de grupo para dois grupos

tente isso, isso dará nomes 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
fonte
1

Grupos de captura nomeados devem ter nomes distintos, mas como todos os seus grupos de captura devem capturar o mesmo padrão, é melhor não usar grupos de captura nomeados nesse caso, mas simplesmente usar grupos de captura regulares e iterar pelos grupos do objeto de correspondência para imprimir o primeiro grupo que não está vazio:

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())))

Demonstração: https://repl.it/@blhsing/UnevenCheerfulLight

blhsing
fonte