4

I'm trying to test if an input has at least 4 number and 1 character in it. I have this which works, but only when the characters are in the order 0000a, I wanted it to match no matter what the order, so 00a00, a0000, aa00a00a would all match the pattern.

[0-9]{4,}[a-zA-Z]{1,}

What do I need to change? I tried [a-zA-Z0-9]{5,} but then things like aaaaa and 00012 matched, which is no good.

TMH
  • 6,096
  • 7
  • 51
  • 88
  • 2
    Have you looked at these kind of questions: [Regex for alpanumeric with at least 1 number and 1 character](http://stackoverflow.com/questions/7684815/regex-for-alpanumeric-with-at-least-1-number-and-1-character), or [check if string contains both number and letter (at least)](http://stackoverflow.com/questions/4429847/check-if-string-contains-both-number-and-letter-at-least) ? – Robin May 12 '14 at 13:54
  • 2
    Thanks for the link, I've got this which seems to work `^(?=.*[0-9]{4,})(?=.*[a-zA-Z]+)([a-zA-Z0-9]+)$`, I'll have a read up and see what exactly a positive lookahead actually does. – TMH May 12 '14 at 13:57
  • Positive look ahead are usually a very easy way to add (possibly many of) these kind of conditions to a single regex. Your `^(?=.*[0-9]{4,})(?=.*[a-zA-Z]+)([a-zA-Z0-9]+)$` attempt wouldn't match `4a444` though, you can fix it with `^(?=(?:.*\d){4})(?=.*[a-zA-Z])[a-zA-Z0-9]+$` ([Demo](http://regex101.com/r/hR3aN7)) or use @falsetru's shorter answer. – Robin May 12 '14 at 14:07
  • @Robin: Oh sorry, read it wrong. – John Bupit May 12 '14 at 14:10
  • See also my answer to a similar question: [Further modifying regular expression for password](http://stackoverflow.com/a/8461455/433790). Questions like this one get asked a _LOT_. – ridgerunner May 12 '14 at 14:46

2 Answers2

8

Using lookahead assertion:

(?=.*[a-zA-Z])(.*?\d){4,}

Regular expression visualization

Debuggex Demo

Mike Perrenoud
  • 66,820
  • 29
  • 157
  • 232
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • On Debuggex it works fine, but in my code it seems to fail each time, I've used it like `var regex = new RegExp('(?=.*[a-zA-Z])(.*?\d){4,}');`, then I'm using `regex.test(myStringVarHere);` Am I doing something wrong? – TMH May 12 '14 at 14:16
  • @TomHart, Use regular expression literal: `var regex = /(?=.*[a-zA-Z])(.*?\d){4,}/` – falsetru May 12 '14 at 14:18
  • @TomHart, because `'\d' == 'd'`; it matches a letter `d` instead of a digit (`\d`). You should escape the backslash if you want to use string literal form. – falsetru May 12 '14 at 14:18
  • Yes, +1. But, instead of using the ubiquitous (and frequently not necessary) _dot-star_ it is typically more efficient to use a more precise expression like so: `^(?=[^a-zA-Z]*[a-zA-Z])(\D*\d){4,}`. I've also added a start-of-string-anchor to make the non-matching cases much more efficient. – ridgerunner May 12 '14 at 14:37
0

If the formal rules are:

"Numbers >= 4 AND characters == 1"

you'll find no way with regex because the character could be everywhere. In this case you'd need some kind of Pushdown automaton or an algorithm with a counter.

If the rule is "Numbers == 4 AND characters == 1" the following should (not easy to read) may work:

([a-zA-Z][0-9]{4,})|([0-9][a-zA-Z][0-9]{3,})|([0-9]{2,}[a-zA-Z][0-9]{2,})|([0-9]{3,}[a-zA-Z][0-9])|([0-9]{4,}[a-zA-Z])

And if the ruls is "(Numbers + characters) >= 4 AND characters >= 1" this may work:

([a-zA-Z]([a-zA-Z]|[0-9]){4,})|(([a-zA-Z]|[0-9])[a-zA-Z]([a-zA-Z]|[0-9]){3,})|(([a-zA-Z]|[0-9]){2,}[a-zA-Z]([a-zA-Z]|[0-9]){2,})|(([a-zA-Z]|[0-9]){3,}[a-zA-Z]([a-zA-Z]|[0-9]))|(([a-zA-Z]|[0-9]){4,}[a-zA-Z])

But because of bot solutions are quite ugly I'd suggest to use none of them. Implement a small algorithm would be easier and more readable.

Mariano
  • 478
  • 4
  • 9