231

What is the regex to make sure that a given string contains at least one character from each of the following categories.

  • Lowercase character
  • Uppercase character
  • Digit
  • Symbol

I know the patterns for individual sets namely [a-z], [A-Z], \d and _|[^\w] (I got them correct, didn't I?).

But how do I combine them to make sure that the string contains all of these in any order?

4
  • What platform/regex-dialect? Bart's answer is right, but lookahead assertions aren't reliable in JavaScript, for example. Commented Oct 13, 2009 at 12:31
  • Nowhere in particular - I'm learning regex. Is there an alternative that can be used in javascript? Commented Oct 14, 2009 at 8:22
  • @bobince Hey, I am trying to find out why lookahead assertions aren't reliable in Javascript. Is there a writeup on this? Commented Oct 7, 2013 at 21:18
  • @ChrisB: There's a really confusing IE/JScript bug: blog.stevenlevithan.com/archives/regex-lookahead-bug Commented Oct 8, 2013 at 13:49

4 Answers 4

471

If you need one single regex, try:

(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*\W) 

A short explanation:

(?=.*[a-z]) // use positive look ahead to see if at least one lower case letter exists (?=.*[A-Z]) // use positive look ahead to see if at least one upper case letter exists (?=.*\d) // use positive look ahead to see if at least one digit exists (?=.*\W) // use positive look ahead to see if at least one non-word character exists 

And I agree with SilentGhost, \W might be a bit broad. I'd replace it with a character set like this: [-+_!@#$%^&*.,?] (feel free to add more of course!)

Sign up to request clarification or add additional context in comments.

14 Comments

What would happen if i change the last .+ into .*? I couldn't come up with a test case that fails with .*. Are they same in this context? "Zero or more characters" seems to be fine - just seeking confirmation.
@Amarghosh: in this case, it makes no difference. Because of the positive look-aheads, the string already contains at least 4 characters. So it makes no difference to change .+ into .* or even .{4,} for that matter.
If you would be willing to add a little explanation, I am having trouble understanding how the combination of these lookaheads guarantees at least 1 of each character set will be present
@BartKiers What if I don't want any symbol? removing (?=.*[_\W]) doesn't work regex101.com/r/jH9rK1/1
@ikertxu, try something like this: ^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?!.*[&%$]).{6,}$
|
45

Bart Kiers, your regex has a couple issues. The best way to do that is this:

(.*[a-z].*) // For lower cases (.*[A-Z].*) // For upper cases (.*\d.*) // For digits (.*\W.*) // For symbols (non-word characters) 

In this way you are searching no matter if at the beginning, at the end or at the middle. In your have I have a lot of troubles with complex passwords.

2 Comments

You're not checking for symbols as the OP requested.
This will only wotk if lower case, upper case and digit are found in this order. For example ist does not work with 111aaqBBB
15

Bart Kiers solution is good but it misses rejecting strings having spaces and accepting strings having underscore (_) as a symbol.

Improving on the Bart Kiers solution, here's the regex:

(?=.*\d)(?=.*[a-z])(?=.*[A-Z])((?=.*\W)|(?=.*_))^[^ ]+$

A short explanation:

(?=.*[a-z]) // use positive look ahead to see if at least one lower case letter exists (?=.*[A-Z]) // use positive look ahead to see if at least one upper case letter exists (?=.*\d) // use positive look ahead to see if at least one digit exists (?=.*\W) // use positive look ahead to see if at least one non-word character exists (?=.*_) // use positive look ahead to see if at least one underscore exists | // The Logical OR operator ^[^ ]+$ // Reject the strings having spaces in them. 

Side note: You can try test cases on a regex expression here.

3 Comments

Why reject on space? If this is for the password, space should be also included as per OWASP recommendations
@Janusz'Ivellios'Kamieński Yes, you're right. But the application I was creating at that time required to reject spaces from passwords. Anyways, to allow spaces, simply remove this from the regex: ^[^ ]+$
Got it... just a side note :)
6

You can match those three groups separately, and make sure that they all present. Also, [^\w] seems a bit too broad, but if that's what you want you might want to replace it with \W.

2 Comments

Thanks. I wasn't aware of \W. Just looked it up to find that it matches Not Word. Is there any difference between \W and [\W]? Is [\W] just redundant?
@Amarghosh, yes, \W and [\W] result in the same.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.