Is it possible to require only 3 out of 4 segments defined in a regular expression?

79 Views Asked by At

I need to build a regex for a password requirement. The requirements are:

Password must be a minimum of 8 characters and contain at least 3 out of 4 of the following:

  1. Upper case
  2. Lower case
  3. Number
  4. Special character

I don't know much about regex. I understand the concept, but I've never heard of a way to specifically only require 3 out of 4 of the parts you define. Below is what I have so far. I know that the ? after each positive lookahead means that it allows 0 or 1 occurrence of the specified character class, but how do I require a minimum of 3 out of my 4 requirements?

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

Update: the other answer linked to this question (probably) achieves the same result, but the given answer here is much cleaner and more elegant. I would suggest opening this question back up so that future readers get a more modern solution.

1

There are 1 best solutions below

8
On BEST ANSWER

Here is how you can do this in a single regex without repeating conditions and without using any lookahead:

^(?:[a-z]()|\d()|[A-Z]()|[^\n\w]()){8,}(?:\1\2(?:\3|\4)|\3\4(?:\1|\2))$

RegEx Demo

RegEx Details:

  • ^:
  • (?::
    • [a-z](): Match an lowercase letter and an empty capture #1
    • |: OR
    • \d(): Match a digit and an empty capture #2
    • |: OR
    • [A-Z](): Match an uppercase letter and an empty capture #3
    • |: OR
    • [^\n\w](): Match a special letter and an empty capture #4
  • ){8,}: End non-capture group. Repeat this group 8+ times
  • (?:\1\2(?:\3|\4)|\3\4(?:\1|\2)): In this non-capture group we are making sure that any 3 empty groups were matched earlier
  • $: End