Regular expression to validate username

I'm trying to create a regular expression to validate usernames against these criteria:

  1. Only contains alphanumeric characters, underscore and dot.
  2. Underscore and dot can't be at the end or start of a username (e.g _username / username_ / .username / username.).
  3. Underscore and dot can't be next to each other (e.g user_.name).
  4. Underscore or dot can't be used multiple times in a row (e.g user__name / user..name).
  5. Number of characters must be between 8 to 20.

This is what I've done so far; it sounds it enforces all criteria rules but the 5th rule. I don't know how to add the 5th rule to this:

 ^[a-zA-Z0-9]+([._]?[a-zA-Z0-9]+)*$

^(?=.{8,20}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(?<![_.])$
 └─────┬────┘└───┬──┘└─────┬─────┘└─────┬─────┘ └───┬───┘
       │         │         │            │           no _ or . at the end
       │         │         │            │
       │         │         │            allowed characters
       │         │         │
       │         │         no __ or _. or ._ or .. inside
       │         │
       │         no _ or . at the beginning
       │
       username is 8-20 characters long

If your browser raises an error due to lack of negative look-behind support, use the following alternative pattern:

^(?=[a-zA-Z0-9._]{8,20}$)(?!.*[_.]{2})[^_.].*[^_.]$

I guess you'd have to use Lookahead expressions here. http://www.regular-expressions.info/lookaround.html

Try

^[a-zA-Z0-9](_(?!(\.|_))|\.(?!(_|\.))|[a-zA-Z0-9]){6,18}[a-zA-Z0-9]$

[a-zA-Z0-9] an alphanumeric THEN (

_(?!\.) a _ not followed by a . OR

\.(?!_) a . not followed by a _ OR

[a-zA-Z0-9] an alphanumeric ) FOR

{6,18} minimum 6 to maximum 18 times THEN

[a-zA-Z0-9] an alphanumeric

(First character is alphanum, then 6 to 18 characters, last character is alphanum, 6+2=8, 18+2=20)


As much as I love regular expressions I think there is a limit to what is readable

So I would suggest

new Regex("^[a-z._]+$", RegexOptions.IgnoreCase).IsMatch(username) &&
!username.StartsWith(".") &&
!username.StartsWith("_") &&
!username.EndsWith(".") &&
!username.EndsWith("_") &&
!username.Contains("..") &&
!username.Contains("__") &&
!username.Contains("._") &&
!username.Contains("_.");

It's longer but it won't need the maintainer to open expresso to understand.

Sure you can comment a long regex but then who ever reads it has to rely on trust.......