Viewed   288 times

I've seen the following regular expression around the web.

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

It validates only if the string:

   * contain at least (1) upper case letter
   * contain at least (1) lower case letter
   * contain at least (1) number or special character
   * contain at least (8) characters in length

I'd like to know how to convert this regular expression so that it checks the string to

* contain at least (2) upper case letter
* contain at least (2) lower case letter
* contain at least (2) digits
* contain at least (2) special character
* contain at least (8) characters in length

Well, if it contains at least 2 upper,lower,digits and special characters then I wouldn't need the 8 characters length.

Special characters include:

`~!@#$%^&*()_-+=[]|{};:'".,/<>?

 Answers

5

The best way to adapt that regex is to chuck it out and write some code instead. The required regex would be so long and complicated, you wouldn't be able to read it two hours after you wrote it. The equivalent PHP code will be tedious, but at least you'll be able understand what you wrote.

This isn't meant as a slam on you, by the way. Regexes are just barely suitable for password-strength validation in most cases, but your requirements are more complicated than usual, and it's just not worth it. Also, that regex you posted is crap. Never trust regexes you find floating around the web. Or any code, for that matter. Or, heck, anything. :-/

Monday, October 3, 2022
2

You want:

/^(?=.{6,12}$)...

What you're doing is saying: find me any sequence of characters that is followed by:

  • 6-12 characters
  • another sequence of characters that is followed by 2 digits
  • another sequence of characters that is followed by 2 uppercase letters
  • another sequence of characters that is followed by 2 lowercase letters

And all that is followed by yet another sequence of characters. That's why the maximum length isn't working because 30 characters followed by 00AAaa and another 30 characters will pass.

Also what you're doing is forcing two numbers together. To be less stringent than that but requiring at least two numbers anywhere in the string:

/^(?=.{6,12}$)(?=(.*?d){2})(?=(.*?[A-Z]){2})(?=(.*?[a-z]){2})/

Lastly you'll note that I'm using non-greedy expressions (.*?). That will avoid a lot of backtracking and for this kind of validation is what you should generally use. The difference between:

(.*d){2}

and

(.*?d){2}

Is that the first will grab all the characters with .* and then look for a digit. It won't find one because it will be at the end of the string so it will backtrack one characters and then look for a digit. If it's not a digit it will keep backtracking until it finds one. After it does it will match that whole expression a second time, which will trigger even more backtracking.

That's what greedy wildcards means.

The second version will pass on zero characters to .*? and look for a digit. If it's not a digit .*? will grab another characters and then look for a digit and so on. Particularly on long search strings this can be orders of magnitude faster. On a short password it almost certainly won't make a difference but it's a good habit to get into of knowing how the regex matcher works and writing the best regex you can.

That being said, this is probably an example of being too clever for your own good. If a password is rejected as not satisfying those conditions, how do you determine which one failed in order to give feedback to the user about what to fix? A programmatic solution is, in practice, probably preferable.

Thursday, October 27, 2022
 
har07
 
4

Don't use regular expressions to parse HTML, it's a bad idea as HTML is not a regular language... You can use other methods such as tidy or the built in DOMDocument to parse it easily without regular expressions

If you insist, what you're looking for is reluctant matching (instead of greedy)

change * to *?

See this post about the difference and this one on why it's a bad idea to try and parse html with regular expressions

Wednesday, December 21, 2022
4

I find that doing it in one big regex is a bit of a code maintenance nightmare. Splitting it up is far easier to figure out for someone else looking at your code, and it allows you to give more specific error messages as well.

$uppercase = preg_match('@[A-Z]@', $password);
$lowercase = preg_match('@[a-z]@', $password);
$number    = preg_match('@[0-9]@', $password);

if(!$uppercase || !$lowercase || !$number || strlen($password) < 8) {
  // tell the user something went wrong
}
Friday, September 9, 2022
 
whyjava
 
4

In other words, you want a password that doesn't just contain one "class" of characters. Then you can use

^(?![a-z]*$)(?![A-Z]*$)(?!d*$)(?!p{P}*$)(?![^a-zA-Zdp{P}]*$).{6,}$

Explanation:

^           # Start of string
(?![a-z]*$) # Assert that it doesn't just contain lowercase alphas
(?![A-Z]*$) # Assert that it doesn't just contain uppercase alphas
(?!d*$)    # Assert that it doesn't just contain digits
(?!p{P}*$) # Assert that it doesn't just contain punctuation
(?![^a-zA-Zdp{P}]*$) # or the inverse of the above
.{6,}       # Match at least six characters
$           # End of string
Friday, October 28, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :