如何在Python Regex中重复后匹配不是字符

时间:2017-08-07 03:36:33

标签: python regex python-3.x

我正在尝试匹配以一个或两个1开头的任何数字,但是如果紧跟在那之后的一个或两个1,那么就没有。

我希望Regex符合以下条件:

1, 12, 11, 123, 111, 1113, 1123, 143, 1153, etc.

但我希望正则表达式匹配:

0, 01, 21, 13, 113, 134, 11333, 135655..., etc.

正如你所看到的,数字必须从1开始。它可以从一个1或两个1开始,但它必须以至少一个1开始。在1(s)之后,不能有一个3.之后一个或两个1,如果1之后没有3,则可以有任意数字(1632362382983598235有效)。

我尝试了以下模式(以及更多模式),但它们都不起作用:

re.fullmatch(r'^1{1,2}[^3].*','113')  # This matches '113' (it shouldn't), doesn't match '13' (as it shouldn't), but it also doesn't match '1' (it should)
re.fullmatch(r'^(11[^3]|1[^3]).*','113')  # Same as above
re.fullmatch(r'^(11(?!3))|(1(?!3)).*','113')  # This one actually allows '1', but it still allows '113'

(我知道'。*'匹配任何字符,而不仅仅是数字。)

我做错了什么?

4 个答案:

答案 0 :(得分:1)

这可以完成工作(try it):

(^1{1,2}[^13].*)|(^1{3}.*)|^1{1,2}

左边部分包括从1或2开始的情况(并确保1和3都不跟随);中间部分涵盖了从3个开始的情况(然后我们不关心接下来的事情);正确的部分包括' 1'和' 11'。

答案 1 :(得分:1)

如果我效仿WiktorStribiżew基于setTimeout(doParsing, 0)的方法,并且避开re.match(),那么为什么以下明显的简单模式不能满足需要:

re.fullmatch()

即。如果数字以1以外的数字开头,或以13开头或以113开头,则拒绝它。我们依靠这种模式来简单地拒绝坏的前缀,不一定完全接受好的数字。

这个问题是否需要“原子组/占有量词解决方法”?

答案 2 :(得分:1)

我想我找到了一个更好的解决方案(至少对我的需求而言)。我使用了负面预测:

^(?!1{1,2}3)1{1,2}.*

说明:

^              # Beginning of string
(?!1{1,2}3)    # Don't match if ^ is followed by one or two 1s and a 3
1{1,2}         # One to two 1s
.*             # Anything else is accepted

最好的部分是它与perigon的答案相同,但它更短,更容易阅读,更容易适应。

修改

cdlane部分给了我一个很棒的解决方案。我将它改编为re.fullmatch,它比我原来的1个字节更容易适应和缩短:

^(?![^1]|1{1,2}3).*

说明:

^         # Beginning of string
(?!       # Do not match if
[^1]      # The string starts with anything other than 1
|1{1,2}3) # Or there is one or two 1s followed by a 3
.*        # Anything else is accepted

对于任何感兴趣的人,您还可以通过插入以下内容轻松限制最初可能出现的1的数量:

|1{limit,}

像这样:

^(?![^1]|1{1,2}3|1{3,}).*

如果你想改变必须出现的1的数量,而不是:

[^1]

使用:

(?!1{minimum,}

把它放在一起:

^(?!(?!1{4,})|1{4,5}3|1{6,}).*

答案 3 :(得分:0)

您可以在此处使用原子组/占有量词解决方法。它涉及一个积极的前瞻,在其模式周围有一个捕获组,然后是对该值的反向引用。它会阻止回溯到11?,而负向前瞻只会在31之后检查11

^(?=(11?))\1(?!3)

请参阅regex demo

<强>详情

  • ^ - 字符串开头
  • (?=(11?)) - 如果在字符串的开头有111,则会检查(但不会推进其索引或将捕获的值添加到匹配值)并将值捕获到第1组
  • \1 - 对第1组值的反向引用(现在,它是原子的,不会回溯到模式中(这是模式的问题)
  • (?!3) - 如果在当前位置右侧立即发现3,则表示匹配失败的否定前瞻。

在Python中,使用原始字符串文字声明正则表达式:

import re
rx = r'(?=(11?))\1(?!3)'
s = '113'
m = re.match(rx, s)
if m:
    print("Valid")
else:
    print("Invalid")

查看online demo。请注意,^无需与re.match一起使用,因为该方法仅在字符串的开头找到匹配项。