1

How can I construct a regular expression such that a password field I am making contains at least one number, at least one lowercase letter and at least one uppercase letter where order DOES NOT matter and anything can be in-between these characters?

I have tried this and got something similar: ".*\\d.*[a-z].*[A-Z].*" but that requires that the number comes first, the lowercase second and the uppercase last.

Tim
  • 1,056
  • 4
  • 17
  • 34

3 Answers3

3

One way to achieve this is by

1) First check for the presence of lower case letter by using a separate regex, say regex-1.

2) Next check if it contains numbers by using another regex.

3) Check for any other rule.

Breaking down the rules would solve the issue you are facing and also improves the way you report the error.

Vivek Viswanathan
  • 1,968
  • 18
  • 26
  • How would you do this though? – Tim Apr 21 '12 at 18:27
  • 1
    Have different Strings that hold the regex pattens-"\\d+", "[a-z]+", "[A-Z]+". Now for each pattern, compile and try matching with the password. You would know which regex pattern is failing. If none of them fail, it means the password has passed all the rules. – Vivek Viswanathan Apr 21 '12 at 18:31
3

Yours is not one of the use case regular expressions are particularly good at – if you're stuck with regular expressions, I guess you have no choice but to enumerate the 6 cases:

.*[0-9].*[a-z].*[A-Z].*|.*[0-9].*[A-Z].*[a-z].*|.*[a-z] ... | ...

Check with actual java code would be easier (and more readable):

String  password = "";
boolean complies = password.matches(".*[0-9].*")
                && password.matches(".*[a-z].*")
                && password.matches(".*[A-Z].*");

or (uglier, but possibly faster):

boolean complies = false;
{
    boolean hasDigit     = false;
    boolean hasLowercase = false;
    boolean hasUppercase = false;
    for (int i = 0; i < password.length(); i++) {
        char c = password.charAt(i);
        hasDigit     |= '0' <= c && c <= '9';
        hasLowercase |= 'a' <= c && c <= 'z';
        hasUppercase |= 'A' <= c && c <= 'Z';
        if (hasDigit && hasLowercase && hasUppercase) {
            complies = true;
            break;
        }
    }
}
giorgiga
  • 1,758
  • 12
  • 29
  • Potentially stupid question, but what does the '?:' mean in front of the '.*' on your solution? I only use '.*' at the beginning of my attempt, is this incorrect? – Tim Apr 21 '12 at 19:07
  • (?: ... ) is a non-capturing group - I added those to group the alternatives (?:alt1)|(?:alt2) - honestly I didn't check if the whole thing works also without grouping (I tend to use more groups than needed) – giorgiga Apr 21 '12 at 19:41
  • Here's the doc about groups and capturing http://docs.oracle.com/javase/1.4.2/docs/api/java/util/regex/Pattern.html#cg – giorgiga Apr 21 '12 at 19:43
  • I did it without it and it seems to work. I just don't really understand what you would use **'?:'** for in the first place. Could you explain what it is doing please in this case please and why you've added it to each alternative? – Tim Apr 21 '12 at 19:43
  • 1
    Did a few tests - seems to work without groups too. Adding explicit grouping instead of relying on built-in precedence is just a habit of mine (not limited to regexes) personally I find 1+(5*4) clearer than 1+5*4. I'm removing the grouping above, sicne it's more confusing than useful :) – giorgiga Apr 21 '12 at 20:08
  • could you not leave in the brackets so it's like: **(.*\\d.*[a-z].*[A-Z].*)|(.*\\d.*[A-Z].*[a-z].*)|...**. It also seems to work, just wondering if it is okay or if you need the **'?:'** with the brackets. – Tim Apr 21 '12 at 22:10
  • 1
    ( ... ) is a capturing group, (?: ... ) is a non-capturing one - they are almost the same, the only difference is that capturing groups may be back-referenced, while non-capturing can't – giorgiga Apr 21 '12 at 22:55
1

Regex is not suited for complicated scenarios like this. The only way you can implement it in regex is by listing all possibilities:

(abc|acb|bac|bca|cab|cba)

Where a, b, and c are sub-patterns that represent your individual requirements, e.g. abc would be .*\\d.*[a-z].*[A-Z].*, bac stands for [a-z].*.*\\d.*[A-Z].*, etc. Good luck in maintaining such a monster. My tip would be to create java constants for each of the parts:

private static final String DI = "\\d+";
private static final String LO = "[a-z]+";
private static final String UP = "[A-Z]+";
private static final String WI = ".*";
private static final String OR = "|";
private static final Pattern REGEX = Pattern .compile(
    WI + DI + WI + LO + WI + UP + WI + OR
  + WI + LO + WI + DI + WI + UP + WI + OR
  // etc
);
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588