user2609980
user2609980

Reputation: 10514

Regex without positive lookahead to match password with one uppercase, one lowercase, one digit and at least eight characters

I am looking for a regex that matches strings (i.e., passwords) that have at least 8 characters, at least one upper case character, at least one lower case character, and at least one number.

A regex that works (with the help of here) would be:

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

This regex uses positive lookahead (?=). This is an expensive operation. Is it possible to make this regex without using positive lookahead?

Upvotes: 2

Views: 2583

Answers (1)

Mariano
Mariano

Reputation: 6511

at least 8 characters, at least one upper case character, at least one lower case character, and at least one number

It is kind of like a puzzle

Ok, I'm going to take this as a puzzle, provided it's understood that:

  • Coding without regex would be more efficient.
  • A lookahead is NOT sinificantly expensive compared to the cost of using regex on its own.

And that this solution may be even more expensive than using a lookahead.

Description

  1. We can use a subpattern like

    \A(?:[A-Z]|[a-z]|[0-9]|.){8,}
    

    to check there are at least 8 characters in the subject, while providing the 4 options (uppercase, lowercase, digit, or some other character).

  2. Then, we'll create a backreference for the first 3 required options:

    \A(?:(?<upper>[A-Z])|(?<lower>[a-z])|(?<digit>[0-9])|.){8,}
    
  3. And finally, we'll use an IF clause to check that each group was captured:

    (?(upper)  (?#check 2nd condition)  |  (?#make it fail)  )
    

    using (?!) to make it fail if any of the conditions isn't met:

    (?(upper)(?(lower)(?(digit)|(?!))|(?!))|(?!))
    

Regex

\A                        #beggining of string
(?>                       #MAIN iteration (atomic only for efficiency)
    (?<upper>[A-Z])       #  an uppercase letter
  |                       # or
    (?<lower>[a-z])       #  a lowercase letter
  |                       # or
    (?<digit>[0-9])       #  a digit
  |                       # or
    .                     #  anything else
){8,}?                    #REPEATED 8+ times
                          #
                          #CONDITIONS:
(?(upper)                 # 1. There must be at least 1 uppercase
    (?(lower)             #    2. If (1), there must be 1 lowercase
        (?(digit)         #       3. If (2), there must be 1 digit
          | (?!)          #          Else fail
        )                 #
      | (?!)              #       Else fail
    )                     #
  | (?!)                  #    Else fail
)                         #

One-liner:

\A(?>(?<upper>[A-Z])|(?<lower>[a-z])|(?<digit>[0-9])|.){8,}?(?(upper)(?(lower)(?(digit)|(?!))|(?!))|(?!))

regex101 demo

Upvotes: 2

Related Questions