The best way to match at least three out of four regex requirements

大憨熊 提交于 2021-02-04 07:12:51

问题


In password strategy, there are 4 requirements. It should contains any three of the following

  1. lower case.
  2. upper case.
  3. numeric.
  4. special character.

The following regex will match all cases

^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{4,8}$

I know I can use '|' to declare all combinations, however, that will produce a supper long regex. What is the best way to replace '|' so that it can check if the input contains any of three conditions in the combination?


回答1:


If you're using a PCRE flavor, the following one could suit your needs (formatted for readability):

^(?:
    ((?=.*\d))((?=.*[a-z]))((?=.*[A-Z]))((?=.*[^a-zA-Z0-9]))|
    (?1)      (?2)         (?3)                             |
    (?1)      (?2)                      (?4)                |
    (?1)                   (?3)         (?4)                |
              (?2)         (?3)         (?4)
).{4,8}$

Regular expression visualization

One-lined:

^(?:((?=.*\d))((?=.*[a-z]))((?=.*[A-Z]))((?=.*[^a-zA-Z0-9]))|(?1)(?2)(?3)|(?1)(?2)(?4)|(?1)(?3)(?4)|(?2)(?3)(?4)).{4,8}$

Demo on Debuggex


JavaScript regex flavor does not support recursion (it does not support many things actually). Better use 4 different regexes instead, for example:

var validate = function(input) {
    var regexes = [
        "[A-Z]",
        "[a-z]",
        "[0-9]",
        "[^a-zA-Z0-9]"
    ];    
    var count = 0;
    for (var i = 0, n = regexes.length; i < n; i++) {
        if (input.match(regexes[i])) {
            count++;
        }
    }    
    return count >=3 && input.match("^.{4,8}$");    
};



回答2:


Sure, here's a method which uses a slight modification of the same regex, but with a short bit of code authoring required.

^(?=(\D*\d)|)(?=([^a-z]*[a-z])|)(?=([^A-Z]*[A-Z])|)(?=([a-zA-Z0-9]*[^a-zA-Z0-9])|).{4,8}$

Here you have five capturing groups - Check whether at least 3 of them are not null. A null group for capture effectively indicates that the alternative within the lookahead has been matched, and the capturing group on the left hand side could not be matched.

For example, in PHP:

preg_match("/^(?=(\\D*\\d)|)(?=([^a-z]*[a-z])|)(?=([^A-Z]*[A-Z])|)(?=([a-zA-Z0-9]*[^a-zA-Z0-9])|).{4,8}$/", $str, $matches);

$count = -1; // Because the zero-eth element is never null.
foreach ($matches as $element) {
    if ( !empty($element)) {
        $count += 1;
    }
}

if ($count >= 3) {
    // ...
}

Or Java:

Matcher matcher = Pattern.compile(...).matcher(string);

int count = 0;

if (matcher.matches())
    for (int i = 1; i < 5; i++)
        if (null != matcher.group(i))
            count++;

if (count >= 3)
    // ...


来源:https://stackoverflow.com/questions/26991679/the-best-way-to-match-at-least-three-out-of-four-regex-requirements

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!