【问题标题】:What is the Regular expression for following constraint?以下约束的正则表达式是什么?
【发布时间】:2011-09-02 07:33:22
【问题描述】:

我想要一个密码字符串的正则表达式,该字符串至少包含以下四种字符中的三种:

  • 英文小写字符(a 到 z)。
  • 英文大写字符(A 到 Z)。
  • 以 10 位为基数(0 到 9)。
  • 非字母数字字符(例如 !、$、#、%)。

并且至少应包含 8 个字符。

【问题讨论】:

  • 这看起来确实是一个要求很高的要求(不那么受欢迎的想法)
  • 8 个字符 - 是最大值,还是长度可以小于 8?
  • 字符串的最小长度为 8 个字符。

标签: c# .net regex validation


【解决方案1】:

考虑到这些要求可能产生的怪异a,我怀疑您实际上最好通过 multiple 检查来执行此操作,例如(伪-代码):

def check_password (s):
    if len(s) < 8:
        return false
    rules_followed = 0
    if match (s, "[a-z]") rules_followed++;
    if match (s, "[A-Z]") rules_followed++;
    if match (s, "[0-9]") rules_followed++;
    if match (s, "[!$#%]") rules_followed++;  # et cetera
    if rules_followed < 3:
        return false
    return true

当其他人必须维护您的代码时(或者即使 必须在六个月后维护它),这可能更具可读性。

现在我意识到这可能并不总是可行(例如,您可能会被一个只允许一个正则表达式进行验证的框架所困)。

但是,如果它可能的,我强烈建议你考虑一下。您应该始终尝试优化的第一件事是可读性。


a 你要么最终得到一个巨大的前瞻正则表达式,要么得到一个包含由| 分隔的十六种排序可能性的单个正则表达式。

这些都不可能像简单的代码段那样高效或可读。

【讨论】:

  • +1 比编写正则表达式来测试 4 种类型的所有可能组合更容易。
【解决方案2】:

正则表达式的扩展 PCRE 版本:

/^(?:
  (?=.*[a-z])         # look ahead: at least one from a-z
  (?=.*[A-Z])         # look ahead: at least one from A-Z
  (?=.*[0-9])         # look ahead: at least one from 0-9
  |                   # or...
  (?=.*[a-z])         # look ahead: at least one from a-z
  (?=.*[A-Z])         # look ahead: at least one from A-Z
  (?=.*[^a-zA-Z0-9])  # look ahead: at least one from special chars
  |                   # or...
  (?=.*[a-z])         # look ahead: at least one from a-z
  (?=.*[0-9])         # look ahead: at least one from 0-9
  (?=.*[^a-zA-Z0-9])  # look ahead: at least one from special chars
  |                   # or...
  (?=.*[A-Z])         # look ahead: at least one from A-Z
  (?=.*[0-9])         # look ahead: at least one from 0-9
  (?=.*[^a-zA-Z0-9])  # look ahead: at least one from special chars
  )
  \S{8,}              # at least 8 non-spaces
$/x

【讨论】:

    【解决方案3】:

    首先,这是对 paxdiablo 代码的 C# 翻译:

    public bool Validate(string input)
    {
        if (input == null || input.Length < 8)
            return false;
        int counter = 0;
        if (input.Any(Char.IsLower)) counter++;
        if (input.Any(Char.IsUpper)) counter++;
        if (input.Any(Char.IsDigit)) counter++;
        if (input.Any(c => Char.IsPunctuation(c) || Char.IsSymbol(c))) counter++;
        return counter >= 3;
    }
    

    如果你坚持使用正则表达式,你可以使用类似于Fun With .NET Regex Balancing Groups 的模式:

    ^
    (?=.*[a-z](?<Counter>))?
    (?=.*[A-Z](?<Counter>))?
    (?=.*[0-9](?<Counter>))?
    (?=.*[^a-zA-Z0-9](?<Counter>))?
    (?<-Counter>){3}   # check we've had at least 3 groups
    .{8}
    

    您也可以允许Unicode classes:

    ^
    (?=.*\p{Ll}(?<Counter>))?
    (?=.*\p{Lu}(?<Counter>))?
    (?=.*\p{Nd}(?<Counter>))?
    (?=.*[\p{S}\p{P}](?<Counter>))?
    (?<-Counter>){3}
    .{8}
    

    【讨论】:

    • @Chris - 如果我没记错的话,RegularExpressionValidator 使用的是客户端 JavaScript 正则表达式,它不支持这些功能。另一种选择是它尝试从头到尾匹配,因此您需要.{8,} 而不是.{8},但我认为这不会有帮助。
    【解决方案4】:

    组合学并没有那么糟糕——只有四种方法可以从四种可能性中选择三种;你可以在正则表达式的开头用前瞻来测试那些,然后用实际匹配检查八个字符:

    ^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*\d)|(?=.*[A-Z])(?=.*[a-z])(?=.*[_\W])|(?=.*[A-Z])(?=.*\d)(?=.*[_\W])|(?=.*[a-z])(?=.*\d)(?=.*[_\W])).{8}
    

    (锚定是为了失败时的效率,没有它也可以工作)。

    密码可能无关紧要,但上面的版本可能会针对相同类型的字符多次查看整个字符串,尤其是在失败时。您可能会以牺牲可读性为代价将其分解:

    ^(?:(?=.*[A-Z])(?:(?=.*[a-z])(?:(?=.*\d)|(?=.*[_\W]))|(?=.*\d)(?=.*[_\W]))|(?=.*[a-z])(?=.*\d)(?=.*[_\W])).{8}
    

    【讨论】:

      【解决方案5】:

      我的问题的正确答案:

      正则表达式:

      ^((?=.[\d])(?=.[az])(?=.[AZ])|(?=.[az])(?=.[AZ])(?= .[^\w\d\s])|(?=.[\d])(?=.[AZ])(?=.[^\w\d\s])|(?=.[\ d])(?=.[az])(?=.[^\w\d\s])).{8,30}$

      谢谢。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多