如何匹配最小数量的正则表达式组或断言?

时间:2017-12-01 14:04:56

标签: asp.net regex regex-lookarounds

好的正则表达式书呆子! 我使用正则表达式前瞻断言进行密码验证,类似于此处描述的模式:

\A(?=\w{6,10}\z)(?=[^a-z]*[a-z])(?=(?:[^A-Z]*[A-Z]){3})(?=\D*\d)

但是,我们只想要求4个断言中的任何3个有效 - 不一定都是有效的。有关如何做到这一点的任何想法?

3 个答案:

答案 0 :(得分:2)

要缩短任何类型的模式,请分解:

\A(?:
    (?=\w{6,10}\z) (?=.*[a-z]) (?: (?:.*[A-Z]){3} | .*\d )
   |
    (?=.*\d) (?=(?:.*[A-Z]){3}) (?: .*[a-z] | \w{6,10}\z )
)

请注意,您不需要前瞻来测试最后一个条件。

demo

其他方式,每个条件都是可选的,并使用命名组进行计数(仅限.net):

\A
(?<c>(?=\w{6,10}\z))?
(?<c>(?=[^a-z]*[a-z]))?
(?<c>(?=(?:[^A-Z]*[A-Z]){3}))?
(?<c>(?=\D*\d))?
(?<-c>){3} # decrement c 3 times
(?(c)|(?!$)) # conditional: force the pattern to fail if too few conditions succeed.

demo

答案 1 :(得分:1)

在单个正则表达式中没有“简单”的方法。唯一的方法是定义“四分之三”断言的所有可能的排列 - 例如

\A(?=\w{6,10}\z)(?=[^a-z]*[a-z])(?=(?:[^A-Z]*[A-Z]){3})| # Maybe no digit
\A(?=[^a-z]*[a-z])(?=(?:[^A-Z]*[A-Z]){3})(?=\D*\d)| # Maybe wrong length
\A(?=\w{6,10}\z)(?=(?:[^A-Z]*[A-Z]){3})(?=\D*\d)| # Maybe no lower
\A(?=\w{6,10}\z)(?=[^a-z]*[a-z])(?=\D*\d) # Maybe not enough uppers

然而,这种精神崩溃的正则表达式显然不是一个好的解决方案。

更好的方法是执行四个检查单独(使用正则表达式或其他方式),并计算至少有三个通过的条件。

...但是,让我们回到这里,然后问:为什么你这样做?您正在实施密码熵检查。根据您的模糊规则,以下密码有效

  • AAAa1
  • 密码1
  • LETmein

以下密码无效

  • reallylongsecurepassword8374235359232
  • HorseBatteryStapleCorrect

我强烈建议不要采取这种奇怪的限制性政策。

答案 2 :(得分:1)

最简单的方法是使用单独的正则表达式,并检查其中3/4是否在您的代码语言中成功。在正则表达式中执行此操作的唯一方法是呈现所有情况。话虽这么说,这可能是最容易呈现所有选项的方法(在正则表达式中),因为它允许您在一个位置(定义它们的位置)编辑模式而不是多次(更容易出错)。很少支持正则表达式中的DEFINE构造,但PCRE正则表达式支持。

您还可以让代码生成每个正则表达式排列。见this question about generating all permutations of a list in python

我不知道你为什么要为密码做这件事,它被认为是弊端,但是,既然你要求它,我想我会给你是regex中最简单的解决方案......你真的应该只检查最小长度(如果你想[基于算法]向用户显示你的系统如何安全找到他们的密码那么复杂)。

代码

(?(DEFINE)
   (?<w>(?=\w{6,10}\z))
   (?<l>(?=[^a-z]*[a-z]))
   (?<u>(?=(?:[^A-Z]*[A-Z]){3}))
   (?<d>(?=\D*\d))
)
\A(?:
    (?&w)(?&l)(?&u)|
    (?&w)(?&l)(?&d)|
    (?&w)(?&u)(?&d)|
    (?&l)(?&u)(?&d)
)

注意:上面的正则表达式使用x修饰符(忽略空格),以便我们可以很好地组织内容。