6

I'm trying to create a regex to pattern match (for passwords) where the string must be between 8 and 30 characters, must have at least 2 digits, at least 2 letters (case-insensitive),at least 1 special character, and no spaces.

I've got the spaces and special character matching working, but am getting thrown on the 2 digits and 2 letters because they don't need to be consecutive.

i.e. it should match a1b2c$ or ab12$ or 1aab2c$.

Something like this for the letters?

(?=.*[a-zA-Z].*[a-zA-Z])  // Not sure.

This string below works, but only if the 2 letters are consecutive and the 2 numbers are consecutive..it fails if the letters, numbers, special chars are interwoven.

(?=^.{8,30}$)((?=.*\\d)(?=.*[A-Za-z]{2})(?=.*[0-9]{2})(?=.*[!@#$%^&*?]{1})(?!.*[\\s]))^.* 
Shikiryu
  • 9,920
  • 6
  • 47
  • 74
user2166893
  • 63
  • 1
  • 1
  • 3
  • 5
    Are you absolutely sure you need regexp for this? – Scorpil Mar 13 '13 at 18:54
  • I would argue against regex and just have individual checks for each one with if statements and some sort of string.contains() function. gparyani has given a solution that seems to be a much nicer fit than regex – user1751547 Mar 13 '13 at 19:13
  • It looks like I need regex since this is part of a Liferay configuration. – user2166893 Mar 13 '13 at 20:14

4 Answers4

7

If you don't want letters to have to be consecutive (?=.*[a-zA-Z].*[a-zA-Z]) is correct approach. Same goes to digits (?=.*\\d.*\\d) or (?=(.*\\d){2}).

Try this regex

(?=^.{8,30}$)(?=(.*\\d){2})(?=(.*[A-Za-z]){2})(?=.*[!@#$%^&*?])(?!.*[\\s])^.*
Pshemo
  • 113,402
  • 22
  • 170
  • 242
  • @AlexChannelmeter Which part requires explanation? Anyway I am guessing you may be looking for: [Regexp Java for password validation](https://stackoverflow.com/q/3802192). Also maybe this will help a little [Reference - What does this regex mean?](https://stackoverflow.com/q/22937618) – Pshemo May 20 '20 at 00:58
4

Your guess would be pretty accurate. It can be made to look a little more elegant with parens.

(?=(.*[a-zA-Z]){2})

Sounds like you are on the right track though.

António Almeida
  • 8,450
  • 5
  • 54
  • 59
Deadron
  • 4,667
  • 1
  • 15
  • 26
3

Use a loop to traverse the string:

/**
 * Checks to see if the specified string has between 8 and 30 characters, has at least 2 digits, at least 2 letters, at least one special character, and no spaces.
 * @param s the String to be checked
 * @return s, if it passes the above test
 * @throws IllegalArgumentException if it does not
 */
public static String check(String s)
{
    IllegalArgumentException invalid = new IllegalArgumentException();
    if(s.length() < 8 || s.length() > 30)
        throw invalid;
    int letters = 0, numbers = 0, specialChars = 0;
    for(char c : s.toCharArray())
    {
        if(c == ' ')
            throw invalid;
        else if(Character.isLetter(c))
            ++letters;
        else if(Character.isDigit(c))
            ++numbers;
        else
            ++specialChars;

    }
    if(letters < 2 || numbers < 2 || specialChars < 1)
        throw invalid;
    return s;
}
gparyani
  • 1,872
  • 3
  • 24
  • 35
0

i observe your examples that you provide are not 8 to 30 characters

try this pattern once if you want 8-30 characters

 (?=[^\s]*[^\sa-zA-Z0-9][^\s]*)(?=[^\s]*[a-zA-Z][^\s]*[A-Za-z][^\s]*)(?=[^\s]*\d[^\s]*\d[^\s]*)[^\s]{8,30}
Civa
  • 1,688
  • 1
  • 14
  • 29