225

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?

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

4 Answers4

455

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!)

Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
  • 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 Oct 14 '09 at 09:49
  • 1
    @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. – Bart Kiers Oct 14 '09 at 10:03
  • 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 – Sharlike Jul 22 '13 at 19:46
  • @Sharlike, I don't know how to rephrase it in order to make it easier to understand. Especially in these small comment-boxes. I recommend you read this: http://www.regular-expressions.info/lookaround.html and if you still have question, simply post a question of your own. Good luck! – Bart Kiers Jul 22 '13 at 19:57
  • 1
    @BartKiers What if I don't want any symbol? removing `(?=.*[_\W])` doesn't work http://regex101.com/r/jH9rK1/1 – orloxx Oct 20 '14 at 15:51
  • 7
    @ikertxu, try something like this: `^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?!.*[&%$]).{6,}$` – Bart Kiers Oct 21 '14 at 08:28
  • @BartKiers Thanks,is it possible to achieve a similar regex with minimum count. ex: 2 uppercase, 2 lowercase, 2 numeric & specific special character (%aAa1A2$) should be valid. sorry, Tried many solution but not to able to achieve.. I tried the following modified RegEx of your ^(?=.*[a-z]{2})(?=.*[A-Z]{2})(?=.*\d{2})(?=.*([%]{2})).{8,10}$. – Ferdy Jun 17 '16 at 15:01
  • How to make a strict check for each of the above along with the condition that an alphabet should be in the start followed by a number ending with a special character finally? – Ramachandra A Pai Dec 14 '18 at 06:35
  • @RamachandraAPai comments are not well suited for asking (or answering) questions. Feel free to create a question of your own. – Bart Kiers Dec 14 '18 at 08:42
  • In my case was atlest 1 lower, atleast 1 upper, atleast 1 digit OR 1 symbol and min length 8. (?=.*[a-z])(?=.*[A-Z])(?=.*(\d|\W)).{8,} – b00sted 'snail' Jun 04 '19 at 08:33
  • The example contains `(?=.*\W)` while the explanation contains `(?=.*\W])`. I think maybe the explanation has a typo - a rogue `]`? – devklick Feb 05 '22 at 18:30
  • @devklick yes, that `]` should't be there – Bart Kiers Feb 05 '22 at 20:15
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.

Elijah Mock
  • 587
  • 8
  • 21
Juan Furattini
  • 776
  • 6
  • 9
  • 6
    You're not checking for symbols as the OP requested. – Janosh May 25 '18 at 12:56
  • 6
    This will only wotk if lower case, upper case and digit are found in this order. For example ist does not work with 111aaqBBB – gsouf May 01 '21 at 10:52
13

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.

Ensei_Tankado
  • 133
  • 2
  • 6
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.

SilentGhost
  • 307,395
  • 66
  • 306
  • 293