7

I can't figure out javascript regex that would satisfy all those requirements:

The string can only contain underscores and alphanumeric characters. It must begin with a letter, not include spaces, not end with an underscore, and not contain two consecutive underscores.

This is as far as I came, but 'not containing consecutive underscores' part is the hardest to add.

^[a-zA-Z][a-zA-Z0-9_]+[a-zA-Z0-9]$
Mariusz
  • 599
  • 1
  • 9
  • 21

4 Answers4

14

You could use multiple lookaheads (neg. ones in this case):

^(?!.*__)(?!.*_$)[A-Za-z]\w*$

See a demo on regex101.com.


Broken down this says:
^           # start of the line
(?!.*__)    # neg. lookahead, no two consecutive underscores (edit 5/31/20: removed extra Kleene star)
(?!.*_$)    # not an underscore right at the end
[A-Za-z]\w* # letter, followed by 0+ alphanumeric characters
$           # the end


As JavaScript snippet:

let strings = ['somestring', '_not_this_one', 'thisone_', 'neither this one', 'but_this_one', 'this__one_not', 'this_one__yes']

var re = /^(?!.*__)(?!.*_$)[A-Za-z]\w*$/;
strings.forEach(function(string) {
    console.log(re.test(string));
});

Please do not restrain passwords!

Jan
  • 42,290
  • 8
  • 54
  • 79
3

You can also use

^[a-zA-Z]([a-zA-Z0-9]|(_(?!_)))+[a-zA-Z0-9]$

Demo

The only change comparing to your regex is changing [a-zA-Z0-9_] to [a-zA-Z0-9]|(_(?!_)). I removed underscore from the character set and allow it in the second part of the alternative if it's not followed by another one.

(?!_) is negative lookahead meaning that _ cannot be next character

mrzasa
  • 22,895
  • 11
  • 56
  • 94
  • Does not work with `a` or `b` or `ab`, see https://regex101.com/r/QgbEYV/2 (meaning the string must be at least 3 characters long, dunno if this is a requirement or not). – Jan Feb 14 '18 at 14:08
1

See regex in use here

^[a-z](?!\w*__)(?:\w*[^\W_])?$
  • ^ Assert position as the start of the line
  • [a-z] Match any lowercase ASCII letter. The code below adds the i (case-insensitive) flag, thus this also matches the uppercase variables
  • (?!\w*__) Negative lookahead ensuring two underscores do not exist in the string
  • (?:\w*[^\W_])? Optionally match the following
    • \w* Match any number of word characters
    • [^\W_] Match any word character except _. Explained: Match anything that is not not a word character, but not _ (since it's in the negated set).
  • $ Assert position at the end of the line

let a = ['somestring', '_not_this_one', 'thisone_', 'neither this one', 'but_this_one', 'this__one_not', 'this_one__yes']
var r = /^[a-z](?!\w*__)(?:\w*[^\W_])?$/i

a.forEach(function(s) {
    if(r.test(s)) console.log(s)
});
ctwheels
  • 21,901
  • 9
  • 42
  • 77
0

Even simpler version, without lookaround (so also usable with regex flavours that don't support them such as POSIX ERE, or even sed-style regex with simple alterations):

^[a-zA-Z](_?[a-zA-Z0-9]+)*$
jhnc
  • 11,310
  • 1
  • 9
  • 26