OK, I've now worked out how to do it almost perfectly. For explanation purposes I've split the expression into three lines, but if you will use it it will have to written in one line like below:
(?!^[a-zA-Z0-9\.\-_]{1,2}$)(?!^[a-zA-Z0-9\.\-_]{16,}$)(?!^[0-9]{3,15}$)^(?:[a-zA-Z0-9](?:\.|_|-)?)+[a-zA-Z0-9]$
Explanation:
There is a grouping operator for regular expressions that is called zero-width negative lookahead (?!
< subexpression >). What does that mean? Let's dissect this monster description:
- Zero-width lookahead
- This essentially means that this matching expression will not consume any characters. So when this expression has matched any following non zero-width expression will start of at the same place in the string as the zero-width regular expression did.
- Negative
- This signifies that all following expressions can only match, if this expression did not previously produce any matches.
(?!^[a-zA-Z0-9\.\-_]{1,2}$) // This expression will assert that a username with characters a-z A-Z 0-9 . _ and - is no less than three characters long
(?!^[a-zA-Z0-9\.\-_]{16,}$) // This expression will assert that a username with characters a-z A-Z 0-9 . _ and - is no longer than 15 characters
(?!^[0-9]{3,15}$) // This expression will assert that a username will not be made up of only digits [0-9]
^(?:[a-zA-Z0-9](?:\.|_|-)?)+[a-zA-Z0-9]$
Given the three assertions at the beginning of the regular expression we can now be sure that there are at least 3 characters and no more than 15 and that a username of only digits is also disallowed. The ^ and $ in the assertions are important here.
Now lets have a look at the final part which is also broken into several lines to better annotate it:
^
(?:[a-zA-Z0-9]
(?:\.|_|-)?
)+
[a-zA-Z0-9]
$
I do hope you could follow my explanations and found this solution helpful. The only drop of bitternes to me is that we now have an additional constraint:
While it is true that the each of the characters '.', '-' and '_' may appear no more than once in row (valid constraint from OP), but now none of the characters in the class [\.\-_] can appear one after another (new constraint introduced through solution).
Regards,
— Manfred