0

I've started with [^\w\\d]*(([0-9]+.*[A-Za-z]{3}.*)|[A-Za-z]+.*([0-9]+.*)) but this requires all letters to be together.

T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
  • 3
    Why not use 2 regex to make things easier – Ed Heal Aug 31 '16 at 18:26
  • Use lookaheads. There are plenty of examples here on SO to get you started. –  Aug 31 '16 at 18:30
  • A single regex is going to be ugly because the digit could be in any of four places relative to the letters. Best to use three regexes. One for basic content, one for digit rule, one for letter rule. – Raymond Chen Aug 31 '16 at 18:32
  • For those interested in why this question was marked as a dup, see http://meta.stackoverflow.com/questions/285733/should-give-me-a-regex-that-does-x-questions-be-closed. –  Aug 31 '16 at 18:33
  • If this isn't a password validation question, it's close enough. The [duplicate question](http://stackoverflow.com/questions/5142103/regex-to-validate-password-strength) I've chosen has different specific requirements, but it demonstrates the techniques you need. – Alan Moore Aug 31 '16 at 19:20
  • Thank you all for the feedback. I understand. It is a username validation by the way. Thanks for the link to the duplicate question! – Carlos F. Montano Aug 31 '16 at 21:53

2 Answers2

2

You could use positive lookahead:

^(?=(.*[a-zA-Z]){3})(?=(.*\d)).{6,64}$

Break-down:

  • (?=(.*[a-zA-Z]){3}): look ahead requiring 3 letters to occur somewhere
  • (?=(.*\d)): look ahead requiring 1 digit to occur somewhere
  • .{6,64}: 6 - 64 characters (no newlines)
  • ^: match at the start of the string
  • $: match the end of the string

Test cases:

var re = /^(?=(.*[a-zA-Z]){3})(?=(.*\d)).{6,64}$/;
// tests: all these should match
var positives = ['abc1.#','1abc1#','@1abc?','@a1bc)'];
// execute tests
test(positives);

// negative tests: all these should not match
var negatives = ['abc1.','1a@c1#','@<abc?'];
// execute tests
test(negatives);

function test(tests){
  for (var str of tests) {
    console.log(`testing "${str}" => ${re.test(str)}`);
  }  
} 
Christoph
  • 46,101
  • 18
  • 92
  • 121
trincot
  • 211,288
  • 25
  • 175
  • 211
  • Indeed Christoph is right, it fails to validate the legth. I am taking care of the length part by adding minLength and maxLength validators in Angular 2. Then I am actually having to write a custom animation as the requirements call for yes, validating with a pattern(s), but also set the error message for the one part in green for a second, then disappear, meaning I have to code for each condition separately. Thanks for all the help! – Carlos F. Montano Aug 31 '16 at 21:56
  • @CarlosF.Montano: *"Indeed Christoph is right, it fails to validate the legth."* That was fixed hours before your comment. Look at the `abc1.` negative test case. The only reason that fails is that it's too short, e.g., the regex is validating the length (that's what the `{6,64}` does). – T.J. Crowder Sep 01 '16 at 06:40
  • I had no idea positive lookaheads could be used like that. Nice one. – T.J. Crowder Sep 01 '16 at 06:40
  • Thanks @Christoph for having improved the testing snippet with a nice template literal! – trincot Sep 01 '16 at 07:36
  • You are very welcome. I really like the lookahead approach, though I would be highly interested in the performance. Seems like a lot of computational effort, especially with multiple lookaheads, doesn't it? – Christoph Sep 01 '16 at 14:17
2

Personally, I'd do this with separate regexes because it's easier to read and maintain (and add new rules):

function testPW(password) {

    // no regex for length needed
    if (password.length < 6 || password.length > 64) return false;

    // new rules could easily be added here
    var patterns = [/(.*[a-zA-Z]){3}/, /\d/];

    for (var pattern in patterns) {
        if (!pattern.match(password)) return false;
    }

    return true;
}

The first pattern, /(.*[a-zA-Z]){3}/, will match any string that contains three ASCII letter characters. The second, /\d/, will match any string that contains a digit.

When taken together, they'll be able to verify that provided passwords meet the requirements you've specified.

Christoph
  • 46,101
  • 18
  • 92
  • 121
Sebastian Lenartowicz
  • 4,340
  • 4
  • 24
  • 36