【问题标题】:python3: extract IP address from compiled patternpython3:从编译模式中提取IP地址
【发布时间】:2020-02-13 10:21:12
【问题描述】:

我想处理日志文件中的每一行,如果行与我的模式匹配,则提取IP 地址。有几种不同类型的消息,在下面的示例中,我使用的是 p1andp2`。

我可以逐行读取文件,并且每一行都匹配每个模式。但 由于可以有更多的模式,我想尽可能高效地做到这一点。我希望将这些模式编译成一个对象,并且每行只匹配一次:

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

但是上面的代码不起作用,它抱怨ip被使用了两次。

实现我的目标最优雅的方式是什么?

编辑:

我根据@Dev Khadka 的回答修改了我的代码。

但我仍在为如何正确处理多个 ip 匹配而苦苦挣扎。下面的代码打印所有匹配 p1 的 IP:

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

但有些行与p1 不匹配。它们匹配p2。即,我得到:

1.2.3.4
None
2.3.4.5
...

当我不知道它是p1p2,...时,如何打印匹配的ip?我想要的只是IP。我不在乎它匹配哪种模式。

【问题讨论】:

  • 你应该提供你的测试数据。

标签: python regex python-3.x


【解决方案1】:

您可以考虑安装出色的regex 模块,该模块支持许多高级正则表达式功能,包括branch reset groups,旨在准确解决您在此问题中概述的问题。分支重置组由(?|...) 表示。分支重置组中不同替代模式中具有相同位置或名称的所有捕获组共享相同的捕获组用于输出。

请注意,在下面的示例中,匹配的捕获组成为命名的捕获组,因此您无需遍历多个组来搜索非空组:

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

演示:https://repl.it/@blhsing/RegularEmbellishedBugs

【讨论】:

  • 这太完美了!谢谢。
  • 999.999.999.999 [Program finished] 这实际上不是一个有效的ip...我们应该使用import ipaddress
【解决方案2】:

你为什么不检查哪个正则表达式匹配?

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

或类似的东西:

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

甚至

num = 1000   # can easily handle millions of patterns =)
for i in range(num) :
    name = 'ip%d' % i
    if name in match :
        print match[name]

【讨论】:

  • 但是如果我有 100 个模式呢?我可以循环执行此操作吗?我可以在 for 循环中迭代 match[i] 吗?
  • @MartinVegter 见上文
  • @MartinVegter 可以轻松处理数百万种模式 =)
  • 我收到一个错误:if match[name] is not None:IndexError: no such group
  • @MartinVegter 尝试改用name in match
【解决方案3】:

那是因为您对两个组使用相同的组名

试试这个,这将给出组名 ip1 和 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 + ')')

【讨论】:

    【解决方案4】:

    命名捕获组必须具有不同的名称,但由于所有捕获组都旨在捕获相同的模式,因此在这种情况下最好不要使用命名捕获组,而只需使用常规捕获组并从打印第一个非空组的匹配对象:

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

    演示:https://repl.it/@blhsing/UnevenCheerfulLight

    【讨论】:

      【解决方案5】:

      为已接受的答案添加 IP 地址有效性。 Altho import ipaddress & import socket 应该是理想的方式,这段代码将解析主机,

      import regex as re 
      from io import StringIO
      
      
      
      def valid_ip(address):
          try:
              host_bytes = address.split('.')
              valid = [int(b) for b in host_bytes]
              valid = [b for b in valid if b >= 0 and b<=255]
              return len(host_bytes) == 4 and len(valid) == 4
          except:
              return False
          
              
          
              
      
      ip_pattern = r'(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
      
      patterns = patterns = [
          'Registration from {ip} - Wrong password',
          'Call from {ip} rejected because extension not found'
      ] 
      
      file = StringIO('''
      Registration from 259.1.1.1 - Wrong password,
          Call from 1.1.2.2 rejected because extension not found
      ''')
      
      pattern = re.compile('(?|%s)' % '|'.join(patterns).format(ip=ip_pattern))
      
      list1 = []
      list2 = []
      
      for line in file:
          match = re.search(pattern, line)
          if match:
              list1.append(match['ip']) # List of ip address 
              list2.append(valid_ip(match['ip'])) # Boolean results of valid_ip 
      
      
      for i in range(len(list1)):
              if list2[i] == False:
                  print(f'{list1[i]} is invalid IP')
              else:
                  print(list1[i])
      
      259.1.1.1 is invalid IP
      1.1.2.2
      
      [Program finished]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-27
        • 1970-01-01
        • 1970-01-01
        • 2016-08-28
        • 2021-10-31
        • 2015-08-24
        • 2011-08-22
        • 1970-01-01
        相关资源
        最近更新 更多