python重复中的正则表达式错误

时间:2018-01-10 17:39:39

标签: python regex python-3.x

在我的代码中,我想回答[('22', '254', '15', '36')]但得到[('15', '36')]。我的正则表达式(?:([0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)\.){3}可能不会运行3次!

import re
def fun(st):
    print(re.findall("(?:([0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)\.){3}([0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)",st))

ip="22.254.15.36"
print(fun(ip))

3 个答案:

答案 0 :(得分:1)

你的正则表达式中只有两个捕获组:

(?:    # non-capturing group
    (  # group 1
        [0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?
    )\.
){3}  
(      # group 2
        [0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?
)

第一组可重复3次并不会使其捕获3次。正则表达式引擎只会返回2个组,并且给定组中的最后一个匹配将填充该组。

如果要将IP地址的每个部分捕获到不同的组中,则必须为每个部分明确定义组:

pattern = (
    r'([0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)\.'
    r'([0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)\.'
    r'([0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)\.'
    r'([0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)')

def fun(st, p=re.compile(pattern)):
    return p.findall(st)

你可以通过一些字符串和列表操作避免那么多的重复:

octet = r'([0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)'
pattern = r'\.'.join([octet] * 4)

接下来,该模式将与25的{​​{1}}部分愉快地匹配。最好将200-255范围的匹配在开头匹配较小的数字:

255

顺便提一下,这仍然允许前导octet = r'(2(?:5[0-5]|[0-4]\d)|[01]?[0-9]{1,2})' pattern = r'\.'.join([octet] * 4) 个数字,但是

如果你所做的只是传入单个IP地址,那么0就是过度杀手,只需使用re.findall()(仅匹配字符串开头)或p.match(),然后返回{ {1}}如果匹配则会产生结果;)

p.search()

请注意,未对周围数据进行验证,因此,如果您尝试从更大的文本中提取IP地址,则无法使用.groups(),并且无法添加{{1} } anchor和匹配可以来自更大数量的八位字节(例如22.22.22.22.22.22)。您必须为此添加一些环视操作符:

def fun(st, p=re.compile(pattern + '$')):
    match = p.match(st)
    return match and match.groups()

答案 1 :(得分:1)

概述

正如我在下面的评论中提到的那样,大多数正则表达式引擎只捕获最后一个匹配。因此,当您执行(...){3}时,仅捕获最后一场比赛:例如用于(.){3}的{​​{1}}只会返回abc

另外,请注意,将正则表达式更改为c的效果要好得多,并且会捕获完整的数字(例如,您最后会在最后一个八位字节中抓取(2[0-4]\d|25[0-5]|[01]?\d{1,2})而不是25 - 除非您将它锚定到最后)。

为您提供一个功能齐全的正则表达式来捕获IP的每个八位字节:

255
然而,就个人而言,我将逻辑与验证分开。下面的代码首先验证字符串的格式,然后在(2[0-4]\d|25[0-5]|[01]?\d{1,2})\.(2[0-4]\d|25[0-5]|[01]?\d{1,2})\.(2[0-4]\d|25[0-5]|[01]?\d{1,2})\.(2[0-4]\d|25[0-5]|[01]?\d{1,2}) 上分割字符串时检查逻辑(没有大于255的八位字节)是否通过。

代码

See code in use here

.

结果:import re ip='22.254.15.36' if re.match(r"(?:\d{1,3}\.){3}\d{1,3}$", ip): print([octet for octet in ip.split('.') if int(octet) < 256])

如果您使用此方法从任意字符串中提取IP,则可以将['22', '254', '15', '36']替换为re.match()re.search()。在这种情况下,您可能需要删除re.findall()并添加一些逻辑,以确保您不匹配$之类的特殊情况:11.11.11.11.11

答案 2 :(得分:0)

我遇到了非常相似的问题。 我使用官方文档找到了两种解决方案。 上面@ctwheels的答案确实提到了该问题的原因,我对此表示感谢,但它没有提供解决方案。 即使尝试向后看向后看,它也不起作用。

  1. 第一个解决方案: re.finditer

re.finditer 遍历匹配对象!

您可以使用每个人的“分组”方法!

    >>> def fun(st):
        pr=re.finditer("(?:([0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)\.){3}([0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)",st)
        for p in pr:
            print(p.group(),end="")

    >>> fun(ip)
    22.254.15.36

或者!!!

  1. 另一种解决方案哈哈:您仍然可以使用findall,但必须将每个组设为非捕获组! (因为主要问题不是 findall ,而是findall使用的 group 函数(众所周知,该函数仅返回最后一个匹配项):

“ re.findall:

...如果模式中存在一个或多个组,则返回组列表”

(Python 3.8手册)

所以:

    >>> def fun(st):
        print(re.findall("(?:(?:[0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)\.){3}(?:[0-1]?[0-9]{0,2}|2?[0-4]?[0-9]|25[0-5]?)",st))

    >>> fun(ip)
    ['22.254.15.36']

玩得开心!