114

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]+)*$
Ωmega
  • 42,614
  • 34
  • 134
  • 203
mohsen dorparasti
  • 8,107
  • 7
  • 41
  • 61

12 Answers12

436
^(?=.{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})[^_.].*[^_.]$
Ωmega
  • 42,614
  • 34
  • 134
  • 203
16

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)

Adam Wolski
  • 5,448
  • 2
  • 27
  • 46
PhilMasteG
  • 3,095
  • 1
  • 20
  • 27
15

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.......

James Kyburz
  • 13,775
  • 1
  • 32
  • 33
9

A slight modification to Phillip's answer fixes the latest requirement

^[a-zA-Z0-9]([._](?![._])|[a-zA-Z0-9]){6,18}[a-zA-Z0-9]$
Patrick McDonald
  • 64,141
  • 14
  • 108
  • 120
  • This gives issue in character limit rule, with the given rule of `{6,18}` it accepts min 8 characters whereas it must be accepting 6, If I make it `{4,18}` then it will accept 6 characters whereas I have defined 4 – Ibrahim Azhar Armar Aug 22 '20 at 13:10
4
private static final Scanner scan = new Scanner(System.in);

public static void main(String[] args) {
    int n = Integer.parseInt(scan.nextLine());
    while (n-- != 0) {
        String userName = scan.nextLine();
        String regularExpression = "^[[A-Z]|[a-z]][[A-Z]|[a-z]|\\d|[_]]{7,29}$";
        if (userName.matches(regularExpression)) {
            System.out.println("Valid");
        } else {
            System.out.println("Invalid");
        }
    }
}
Isurie
  • 310
  • 4
  • 9
3
^(?=.{4,20}$)(?:[a-zA-Z\d]+(?:(?:\.|-|_)[a-zA-Z\d])*)+$

You can test the regex here

FrEqDe
  • 181
  • 2
  • 3
  • 10
  • 2
    This is allowing underscore at the beginning and at the end, which is not wanted. – Toto Nov 08 '19 at 17:47
  • 2
    It also matches infinite length string that contains only underscore. – Toto Nov 08 '19 at 17:50
  • 1
    @Toto, I fixed it. – FrEqDe Nov 08 '19 at 19:43
  • 1
    Works fine, but allows `-` character, I made a little adjustment to it here: ``^(?=.{4,20}$)(?:[a-zA-Z\d]+(?:[._][a-zA-Z\d])*)+$`` which fix it for me, thanks for the code @FrEqDe – Eazy Jun 21 '21 at 14:36
1

^[a-z0-9_-]{3,15}$

^ # Start of the line

[a-z0-9_-] # Match characters and symbols in the list, a-z, 0-9, underscore, hyphen

{3,15} # Length at least 3 characters and maximum length of 15

$ # End of the line

Mehul Jariwala
  • 886
  • 1
  • 9
  • 19
1
const regex = /^moe_(app|lab)[A-Za-z0-9]{3}$/;
const str = `moe_app_baa`;
let m;

if ((m = regex.exec(str)) !== null) {
    // The result can be accessed through the `m`-variable.
    m.forEach((match, groupIndex) => {
        console.log(`Found match, group ${groupIndex}: ${match}`);
    });
}
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
guisse224
  • 11
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 11 '21 at 09:55
1

Stumbled upon this question in another one. Seems not necessary to use that many lookarounds. Following a bit shorter pattern with use of \w which is a short for word character and commonly preset to [A-Za-z0-9_]. To get rid of the underscore, [^\W_] can be used to match [A-Za-z0-9].

^[^\W_](?!.*?[._]{2})[\w.]{6,18}[^\W_]$
  • ^[^\W_] one letter or digit at the start
  • (?!.*?[._]{2}) negative lookahead to disallow __ ._ _. ..
    (for reduing steps on longer inputs, a lazy quantifier is used in .*?)
  • [\w.]{6,18} 6 to 18 characters that can be letters, digits, _ and .
  • [^\W_]$ ending in another letter or digit

See this demo at regex101 (further explanation on the right side)

Note: \w depending on environment/settings can further contain unicode letters and digits.

bobble bubble
  • 16,888
  • 3
  • 27
  • 46
0

Err sorry I generated this from my own library and it uses the syntax valid for Dart/Javascript/Java/Python, but anyway, here goes:

(?:^)(?:(?:[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKlMNOPQRSTUVWXYZ0123456789]){1,1})(?!(?:(?:(?:(?:_\.){1,1}))|(?:(?:(?:__){1,1}))|(?:(?:(?:\.\.){1,1}))))(?:(?:(?:(?:[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKlMNOPQRSTUVWXYZ0123456789._]){1,1})(?!(?:(?:(?:(?:_\.){1,1}))|(?:(?:(?:__){1,1}))|(?:(?:(?:\.\.){1,1}))))){6,18})(?:(?:[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKlMNOPQRSTUVWXYZ0123456789]){1,1})(?:$)

My library code:

var alphaNumeric = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "l", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
var allValidCharacters = new List.from(alphaNumeric);
allValidCharacters.addAll([".", "_"]);

var invalidSequence = (r) => r
  .eitherString("_.")
  .orString("__")
  .orString("..");

var regex = new RegExpBuilder()
  .start()
  .exactly(1).from(alphaNumeric).notBehind(invalidSequence)
  .min(6).max(18).like((r) => r.exactly(1).from(allValidCharacters).notBehind(invalidSequence))
  .exactly(1).from(alphaNumeric)
  .end()
  .getRegExp();

My library: https://github.com/thebinarysearchtree/RegExpBuilder

  • ya it is hilarious :) I would probably just do a simple regular expression and then use contains, startsWith, etc... anyway. OK will fix the numbering. –  Aug 18 '12 at 12:51
0

This one should do the trick:

if (Regex.IsMatch(text, @"
    # Validate username with 5 constraints.
    ^                          # Anchor to start of string.
    # 1- only contains alphanumeric characters , underscore and dot.
    # 2- underscore and dot can't be at the end or start of username,
    # 3- underscore and dot can't come next to each other.
    # 4- each time just one occurrence of underscore or dot is valid.
    (?=[A-Za-z0-9]+(?:[_.][A-Za-z0-9]+)*$)
    # 5- number of characters must be between 8 to 20.
    [A-Za-z0-9_.]{8,20}        # Apply constraint 5.
    $                          # Anchor to end of string.
    ", RegexOptions.IgnorePatternWhitespace))
{
    // Successful match
} else {
    // Match attempt failed
} 
ridgerunner
  • 33,777
  • 5
  • 57
  • 69
-2
function isUserName(val){
    let regUser=/^[a-zA-Z0-9](_(?!(\.|_))|\.(?!(_|\.))|[a-zA-Z0-9]){6,18}[a-zA-Z0-9]$/;
    if(!regUser.test(val)){
      return 'Name can only use letters,numbers, minimum length is 8 characters';
    }
  }
tjjjohnson
  • 3,270
  • 4
  • 31
  • 33