RegExp for matching usernames: min 3 chars, max 20 chars, optional underscore in between chars












7















I'm attempting to match roblox usernames (which follow these guidelines):




  • Minimum of 3 characters


  • Maximum of 20 characters


  • Maximum of 1 underscore


  • Underscore may not be at the beginning or end of the username



I am running on node.js version 10.12.0.



My current RegExp is:
/^([a-z0-9])(w)+([a-z0-9])$/i, but this does not account for the limit of 1 underscore.



List of some unit tests on regex101.com










share|improve this question




















  • 1





    Regular expressions are bad at counting. Did you consider solving this requirement in JavaScript instead?

    – Micha Wiedenmann
    Jan 27 at 19:17











  • @MichaWiedenmann: You don't really need to count for the condition to only have one underscore. It is completely achievable with negated character classes and anchors.

    – Jan
    Jan 27 at 19:31











  • He might have referred to the character limitation, where one may achieve a performance improvement by checking the string with .length in JavaScript.

    – Siavas
    Jan 27 at 20:06











  • While this problem can be solved by a single regex, the code is harder to understand. At least, when a positive lookahead is required to simulate matching two regexes simultaneously str.match(/^(?=regex1$)regex2$/), just use two regexes str.match(/^regex1$/) && str.match(/^regex2$/).

    – user202729
    Jan 28 at 1:57













  • @Jan Yes, it is achievable, but that is not my point.

    – Micha Wiedenmann
    Jan 28 at 7:49
















7















I'm attempting to match roblox usernames (which follow these guidelines):




  • Minimum of 3 characters


  • Maximum of 20 characters


  • Maximum of 1 underscore


  • Underscore may not be at the beginning or end of the username



I am running on node.js version 10.12.0.



My current RegExp is:
/^([a-z0-9])(w)+([a-z0-9])$/i, but this does not account for the limit of 1 underscore.



List of some unit tests on regex101.com










share|improve this question




















  • 1





    Regular expressions are bad at counting. Did you consider solving this requirement in JavaScript instead?

    – Micha Wiedenmann
    Jan 27 at 19:17











  • @MichaWiedenmann: You don't really need to count for the condition to only have one underscore. It is completely achievable with negated character classes and anchors.

    – Jan
    Jan 27 at 19:31











  • He might have referred to the character limitation, where one may achieve a performance improvement by checking the string with .length in JavaScript.

    – Siavas
    Jan 27 at 20:06











  • While this problem can be solved by a single regex, the code is harder to understand. At least, when a positive lookahead is required to simulate matching two regexes simultaneously str.match(/^(?=regex1$)regex2$/), just use two regexes str.match(/^regex1$/) && str.match(/^regex2$/).

    – user202729
    Jan 28 at 1:57













  • @Jan Yes, it is achievable, but that is not my point.

    – Micha Wiedenmann
    Jan 28 at 7:49














7












7








7


1






I'm attempting to match roblox usernames (which follow these guidelines):




  • Minimum of 3 characters


  • Maximum of 20 characters


  • Maximum of 1 underscore


  • Underscore may not be at the beginning or end of the username



I am running on node.js version 10.12.0.



My current RegExp is:
/^([a-z0-9])(w)+([a-z0-9])$/i, but this does not account for the limit of 1 underscore.



List of some unit tests on regex101.com










share|improve this question
















I'm attempting to match roblox usernames (which follow these guidelines):




  • Minimum of 3 characters


  • Maximum of 20 characters


  • Maximum of 1 underscore


  • Underscore may not be at the beginning or end of the username



I am running on node.js version 10.12.0.



My current RegExp is:
/^([a-z0-9])(w)+([a-z0-9])$/i, but this does not account for the limit of 1 underscore.



List of some unit tests on regex101.com







javascript node.js regex






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 27 at 19:50









Siavas

2,08611224




2,08611224










asked Jan 27 at 19:07









Jamie ClarkJamie Clark

515




515








  • 1





    Regular expressions are bad at counting. Did you consider solving this requirement in JavaScript instead?

    – Micha Wiedenmann
    Jan 27 at 19:17











  • @MichaWiedenmann: You don't really need to count for the condition to only have one underscore. It is completely achievable with negated character classes and anchors.

    – Jan
    Jan 27 at 19:31











  • He might have referred to the character limitation, where one may achieve a performance improvement by checking the string with .length in JavaScript.

    – Siavas
    Jan 27 at 20:06











  • While this problem can be solved by a single regex, the code is harder to understand. At least, when a positive lookahead is required to simulate matching two regexes simultaneously str.match(/^(?=regex1$)regex2$/), just use two regexes str.match(/^regex1$/) && str.match(/^regex2$/).

    – user202729
    Jan 28 at 1:57













  • @Jan Yes, it is achievable, but that is not my point.

    – Micha Wiedenmann
    Jan 28 at 7:49














  • 1





    Regular expressions are bad at counting. Did you consider solving this requirement in JavaScript instead?

    – Micha Wiedenmann
    Jan 27 at 19:17











  • @MichaWiedenmann: You don't really need to count for the condition to only have one underscore. It is completely achievable with negated character classes and anchors.

    – Jan
    Jan 27 at 19:31











  • He might have referred to the character limitation, where one may achieve a performance improvement by checking the string with .length in JavaScript.

    – Siavas
    Jan 27 at 20:06











  • While this problem can be solved by a single regex, the code is harder to understand. At least, when a positive lookahead is required to simulate matching two regexes simultaneously str.match(/^(?=regex1$)regex2$/), just use two regexes str.match(/^regex1$/) && str.match(/^regex2$/).

    – user202729
    Jan 28 at 1:57













  • @Jan Yes, it is achievable, but that is not my point.

    – Micha Wiedenmann
    Jan 28 at 7:49








1




1





Regular expressions are bad at counting. Did you consider solving this requirement in JavaScript instead?

– Micha Wiedenmann
Jan 27 at 19:17





Regular expressions are bad at counting. Did you consider solving this requirement in JavaScript instead?

– Micha Wiedenmann
Jan 27 at 19:17













@MichaWiedenmann: You don't really need to count for the condition to only have one underscore. It is completely achievable with negated character classes and anchors.

– Jan
Jan 27 at 19:31





@MichaWiedenmann: You don't really need to count for the condition to only have one underscore. It is completely achievable with negated character classes and anchors.

– Jan
Jan 27 at 19:31













He might have referred to the character limitation, where one may achieve a performance improvement by checking the string with .length in JavaScript.

– Siavas
Jan 27 at 20:06





He might have referred to the character limitation, where one may achieve a performance improvement by checking the string with .length in JavaScript.

– Siavas
Jan 27 at 20:06













While this problem can be solved by a single regex, the code is harder to understand. At least, when a positive lookahead is required to simulate matching two regexes simultaneously str.match(/^(?=regex1$)regex2$/), just use two regexes str.match(/^regex1$/) && str.match(/^regex2$/).

– user202729
Jan 28 at 1:57







While this problem can be solved by a single regex, the code is harder to understand. At least, when a positive lookahead is required to simulate matching two regexes simultaneously str.match(/^(?=regex1$)regex2$/), just use two regexes str.match(/^regex1$/) && str.match(/^regex2$/).

– user202729
Jan 28 at 1:57















@Jan Yes, it is achievable, but that is not my point.

– Micha Wiedenmann
Jan 28 at 7:49





@Jan Yes, it is achievable, but that is not my point.

– Micha Wiedenmann
Jan 28 at 7:49












4 Answers
4






active

oldest

votes


















8














You could use



^(?=^[^_]+_?[^_]+$)w{3,20}$


See a demo on regex101.com (there are newline characters for demo purposes)




Broken down this is

^         # start of the string
(?=
^ # start of the string
[^_]+ # not an underscore, at least once
_? # an underscore
[^_]+ # not an underscore, at least once
$ # end of the string
)
w{3,20} # 3-20 alphanumerical characters
$ # end





The question has received quite some attention so I felt to add a non-regex version as well:




let usernames = ['gt_c', 'gt', 'g_t_c', 'gtc_', 'OnlyTwentyCharacters', 'poppy_harlow'];

let alphanumeric = new Set(['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', '_']);

function isValidUsername(user) {
/* non-regex version */
// length
if (user.length < 3 || user.length > 20)
return false;

// not allowed to start/end with underscore
if (user.startsWith('_') || user.endsWith('_'))
return false;

// max one underscore
var underscores = 0;
for (var c of user) {
if (c == '_') underscores++;
if (!alphanumeric.has(c))
return false;
}

if (underscores > 1)
return false;

// if none of these returned false, it's probably ok
return true;
}

function isValidUsernameRegex(user) {
/* regex version */
if (user.match(/^(?=^[^_]+_?[^_]+$)w{3,20}$/))
return true;
return false;
}

usernames.forEach(function(username) {
console.log(username + " = " + isValidUsername(username));
});





I personally think the regex version is shorter and cleaner but it's up to you to decide. Especially the alphanumeric part requires either some comparisons or a regex. With the latter in mind, you could use a regex version altogether.






share|improve this answer





















  • 2





    Nice short solution, though I'm not sure OP's rules would allow other special characters than underscore, such as dash or non-ASCII.

    – Siavas
    Jan 27 at 19:32






  • 2





    This regex does not account for usernames with no underscore.

    – Jamie Clark
    Jan 27 at 19:46











  • @JamieClark: Now it does.

    – Jan
    Jan 27 at 20:37



















4














Could be like this:



^(?=^w{3,20}$)[a-z0-9]+_?[a-z0-9]+$
| | | | | End with any alphanumeric
| | | |
| | | Optional underscore in middle
| | |
| | Start with any alphanumeric
| |
| Any accepted chars
| between 3 and 20 chars.
|
Start of string


The positive lookahead ensures the length will be of minimum 3 chars and maximum 20, and we check for an optional underscore in between one or more characters.



Try it here – I have added unit testing similar to yours as well.









share|improve this answer

































    0














    If you consider using JS functions along with regex this is how i will do.



    In matches i included all the matching strings by neglecting the _ condition and in final i am checking for the _ condition.






    let str = `_vila
    v_v
    v__v
    v_v
    vvvvvv_
    12345678912345678912
    12345678912345678912123456`


    let matches = str.match(/^[a-z0-9]w{1,18}[a-z0-9]$/gm)

    let final = matches.map(e=> (e.split('_').length < 3 ? ({value:e,match:true}) : ({value:e,match:false})))

    console.log(final)








    share|improve this answer































      0














      One more variant: /^[a-z0-9](?:[a-z0-9]|_(?!.*_)){1,18}[a-z0-9]$/i






      share|improve this answer























        Your Answer






        StackExchange.ifUsing("editor", function () {
        StackExchange.using("externalEditor", function () {
        StackExchange.using("snippets", function () {
        StackExchange.snippets.init();
        });
        });
        }, "code-snippets");

        StackExchange.ready(function() {
        var channelOptions = {
        tags: "".split(" "),
        id: "1"
        };
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function() {
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled) {
        StackExchange.using("snippets", function() {
        createEditor();
        });
        }
        else {
        createEditor();
        }
        });

        function createEditor() {
        StackExchange.prepareEditor({
        heartbeatType: 'answer',
        autoActivateHeartbeat: false,
        convertImagesToLinks: true,
        noModals: true,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: 10,
        bindNavPrevention: true,
        postfix: "",
        imageUploader: {
        brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
        contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
        allowUrls: true
        },
        onDemand: true,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        });


        }
        });














        draft saved

        draft discarded


















        StackExchange.ready(
        function () {
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54391861%2fregexp-for-matching-usernames-min-3-chars-max-20-chars-optional-underscore-in%23new-answer', 'question_page');
        }
        );

        Post as a guest















        Required, but never shown

























        4 Answers
        4






        active

        oldest

        votes








        4 Answers
        4






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        8














        You could use



        ^(?=^[^_]+_?[^_]+$)w{3,20}$


        See a demo on regex101.com (there are newline characters for demo purposes)




        Broken down this is

        ^         # start of the string
        (?=
        ^ # start of the string
        [^_]+ # not an underscore, at least once
        _? # an underscore
        [^_]+ # not an underscore, at least once
        $ # end of the string
        )
        w{3,20} # 3-20 alphanumerical characters
        $ # end





        The question has received quite some attention so I felt to add a non-regex version as well:




        let usernames = ['gt_c', 'gt', 'g_t_c', 'gtc_', 'OnlyTwentyCharacters', 'poppy_harlow'];

        let alphanumeric = new Set(['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', '_']);

        function isValidUsername(user) {
        /* non-regex version */
        // length
        if (user.length < 3 || user.length > 20)
        return false;

        // not allowed to start/end with underscore
        if (user.startsWith('_') || user.endsWith('_'))
        return false;

        // max one underscore
        var underscores = 0;
        for (var c of user) {
        if (c == '_') underscores++;
        if (!alphanumeric.has(c))
        return false;
        }

        if (underscores > 1)
        return false;

        // if none of these returned false, it's probably ok
        return true;
        }

        function isValidUsernameRegex(user) {
        /* regex version */
        if (user.match(/^(?=^[^_]+_?[^_]+$)w{3,20}$/))
        return true;
        return false;
        }

        usernames.forEach(function(username) {
        console.log(username + " = " + isValidUsername(username));
        });





        I personally think the regex version is shorter and cleaner but it's up to you to decide. Especially the alphanumeric part requires either some comparisons or a regex. With the latter in mind, you could use a regex version altogether.






        share|improve this answer





















        • 2





          Nice short solution, though I'm not sure OP's rules would allow other special characters than underscore, such as dash or non-ASCII.

          – Siavas
          Jan 27 at 19:32






        • 2





          This regex does not account for usernames with no underscore.

          – Jamie Clark
          Jan 27 at 19:46











        • @JamieClark: Now it does.

          – Jan
          Jan 27 at 20:37
















        8














        You could use



        ^(?=^[^_]+_?[^_]+$)w{3,20}$


        See a demo on regex101.com (there are newline characters for demo purposes)




        Broken down this is

        ^         # start of the string
        (?=
        ^ # start of the string
        [^_]+ # not an underscore, at least once
        _? # an underscore
        [^_]+ # not an underscore, at least once
        $ # end of the string
        )
        w{3,20} # 3-20 alphanumerical characters
        $ # end





        The question has received quite some attention so I felt to add a non-regex version as well:




        let usernames = ['gt_c', 'gt', 'g_t_c', 'gtc_', 'OnlyTwentyCharacters', 'poppy_harlow'];

        let alphanumeric = new Set(['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', '_']);

        function isValidUsername(user) {
        /* non-regex version */
        // length
        if (user.length < 3 || user.length > 20)
        return false;

        // not allowed to start/end with underscore
        if (user.startsWith('_') || user.endsWith('_'))
        return false;

        // max one underscore
        var underscores = 0;
        for (var c of user) {
        if (c == '_') underscores++;
        if (!alphanumeric.has(c))
        return false;
        }

        if (underscores > 1)
        return false;

        // if none of these returned false, it's probably ok
        return true;
        }

        function isValidUsernameRegex(user) {
        /* regex version */
        if (user.match(/^(?=^[^_]+_?[^_]+$)w{3,20}$/))
        return true;
        return false;
        }

        usernames.forEach(function(username) {
        console.log(username + " = " + isValidUsername(username));
        });





        I personally think the regex version is shorter and cleaner but it's up to you to decide. Especially the alphanumeric part requires either some comparisons or a regex. With the latter in mind, you could use a regex version altogether.






        share|improve this answer





















        • 2





          Nice short solution, though I'm not sure OP's rules would allow other special characters than underscore, such as dash or non-ASCII.

          – Siavas
          Jan 27 at 19:32






        • 2





          This regex does not account for usernames with no underscore.

          – Jamie Clark
          Jan 27 at 19:46











        • @JamieClark: Now it does.

          – Jan
          Jan 27 at 20:37














        8












        8








        8







        You could use



        ^(?=^[^_]+_?[^_]+$)w{3,20}$


        See a demo on regex101.com (there are newline characters for demo purposes)




        Broken down this is

        ^         # start of the string
        (?=
        ^ # start of the string
        [^_]+ # not an underscore, at least once
        _? # an underscore
        [^_]+ # not an underscore, at least once
        $ # end of the string
        )
        w{3,20} # 3-20 alphanumerical characters
        $ # end





        The question has received quite some attention so I felt to add a non-regex version as well:




        let usernames = ['gt_c', 'gt', 'g_t_c', 'gtc_', 'OnlyTwentyCharacters', 'poppy_harlow'];

        let alphanumeric = new Set(['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', '_']);

        function isValidUsername(user) {
        /* non-regex version */
        // length
        if (user.length < 3 || user.length > 20)
        return false;

        // not allowed to start/end with underscore
        if (user.startsWith('_') || user.endsWith('_'))
        return false;

        // max one underscore
        var underscores = 0;
        for (var c of user) {
        if (c == '_') underscores++;
        if (!alphanumeric.has(c))
        return false;
        }

        if (underscores > 1)
        return false;

        // if none of these returned false, it's probably ok
        return true;
        }

        function isValidUsernameRegex(user) {
        /* regex version */
        if (user.match(/^(?=^[^_]+_?[^_]+$)w{3,20}$/))
        return true;
        return false;
        }

        usernames.forEach(function(username) {
        console.log(username + " = " + isValidUsername(username));
        });





        I personally think the regex version is shorter and cleaner but it's up to you to decide. Especially the alphanumeric part requires either some comparisons or a regex. With the latter in mind, you could use a regex version altogether.






        share|improve this answer















        You could use



        ^(?=^[^_]+_?[^_]+$)w{3,20}$


        See a demo on regex101.com (there are newline characters for demo purposes)




        Broken down this is

        ^         # start of the string
        (?=
        ^ # start of the string
        [^_]+ # not an underscore, at least once
        _? # an underscore
        [^_]+ # not an underscore, at least once
        $ # end of the string
        )
        w{3,20} # 3-20 alphanumerical characters
        $ # end





        The question has received quite some attention so I felt to add a non-regex version as well:




        let usernames = ['gt_c', 'gt', 'g_t_c', 'gtc_', 'OnlyTwentyCharacters', 'poppy_harlow'];

        let alphanumeric = new Set(['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', '_']);

        function isValidUsername(user) {
        /* non-regex version */
        // length
        if (user.length < 3 || user.length > 20)
        return false;

        // not allowed to start/end with underscore
        if (user.startsWith('_') || user.endsWith('_'))
        return false;

        // max one underscore
        var underscores = 0;
        for (var c of user) {
        if (c == '_') underscores++;
        if (!alphanumeric.has(c))
        return false;
        }

        if (underscores > 1)
        return false;

        // if none of these returned false, it's probably ok
        return true;
        }

        function isValidUsernameRegex(user) {
        /* regex version */
        if (user.match(/^(?=^[^_]+_?[^_]+$)w{3,20}$/))
        return true;
        return false;
        }

        usernames.forEach(function(username) {
        console.log(username + " = " + isValidUsername(username));
        });





        I personally think the regex version is shorter and cleaner but it's up to you to decide. Especially the alphanumeric part requires either some comparisons or a regex. With the latter in mind, you could use a regex version altogether.






        let usernames = ['gt_c', 'gt', 'g_t_c', 'gtc_', 'OnlyTwentyCharacters', 'poppy_harlow'];

        let alphanumeric = new Set(['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', '_']);

        function isValidUsername(user) {
        /* non-regex version */
        // length
        if (user.length < 3 || user.length > 20)
        return false;

        // not allowed to start/end with underscore
        if (user.startsWith('_') || user.endsWith('_'))
        return false;

        // max one underscore
        var underscores = 0;
        for (var c of user) {
        if (c == '_') underscores++;
        if (!alphanumeric.has(c))
        return false;
        }

        if (underscores > 1)
        return false;

        // if none of these returned false, it's probably ok
        return true;
        }

        function isValidUsernameRegex(user) {
        /* regex version */
        if (user.match(/^(?=^[^_]+_?[^_]+$)w{3,20}$/))
        return true;
        return false;
        }

        usernames.forEach(function(username) {
        console.log(username + " = " + isValidUsername(username));
        });





        let usernames = ['gt_c', 'gt', 'g_t_c', 'gtc_', 'OnlyTwentyCharacters', 'poppy_harlow'];

        let alphanumeric = new Set(['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', '_']);

        function isValidUsername(user) {
        /* non-regex version */
        // length
        if (user.length < 3 || user.length > 20)
        return false;

        // not allowed to start/end with underscore
        if (user.startsWith('_') || user.endsWith('_'))
        return false;

        // max one underscore
        var underscores = 0;
        for (var c of user) {
        if (c == '_') underscores++;
        if (!alphanumeric.has(c))
        return false;
        }

        if (underscores > 1)
        return false;

        // if none of these returned false, it's probably ok
        return true;
        }

        function isValidUsernameRegex(user) {
        /* regex version */
        if (user.match(/^(?=^[^_]+_?[^_]+$)w{3,20}$/))
        return true;
        return false;
        }

        usernames.forEach(function(username) {
        console.log(username + " = " + isValidUsername(username));
        });






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Jan 28 at 19:04

























        answered Jan 27 at 19:27









        JanJan

        26k52750




        26k52750








        • 2





          Nice short solution, though I'm not sure OP's rules would allow other special characters than underscore, such as dash or non-ASCII.

          – Siavas
          Jan 27 at 19:32






        • 2





          This regex does not account for usernames with no underscore.

          – Jamie Clark
          Jan 27 at 19:46











        • @JamieClark: Now it does.

          – Jan
          Jan 27 at 20:37














        • 2





          Nice short solution, though I'm not sure OP's rules would allow other special characters than underscore, such as dash or non-ASCII.

          – Siavas
          Jan 27 at 19:32






        • 2





          This regex does not account for usernames with no underscore.

          – Jamie Clark
          Jan 27 at 19:46











        • @JamieClark: Now it does.

          – Jan
          Jan 27 at 20:37








        2




        2





        Nice short solution, though I'm not sure OP's rules would allow other special characters than underscore, such as dash or non-ASCII.

        – Siavas
        Jan 27 at 19:32





        Nice short solution, though I'm not sure OP's rules would allow other special characters than underscore, such as dash or non-ASCII.

        – Siavas
        Jan 27 at 19:32




        2




        2





        This regex does not account for usernames with no underscore.

        – Jamie Clark
        Jan 27 at 19:46





        This regex does not account for usernames with no underscore.

        – Jamie Clark
        Jan 27 at 19:46













        @JamieClark: Now it does.

        – Jan
        Jan 27 at 20:37





        @JamieClark: Now it does.

        – Jan
        Jan 27 at 20:37













        4














        Could be like this:



        ^(?=^w{3,20}$)[a-z0-9]+_?[a-z0-9]+$
        | | | | | End with any alphanumeric
        | | | |
        | | | Optional underscore in middle
        | | |
        | | Start with any alphanumeric
        | |
        | Any accepted chars
        | between 3 and 20 chars.
        |
        Start of string


        The positive lookahead ensures the length will be of minimum 3 chars and maximum 20, and we check for an optional underscore in between one or more characters.



        Try it here – I have added unit testing similar to yours as well.









        share|improve this answer






























          4














          Could be like this:



          ^(?=^w{3,20}$)[a-z0-9]+_?[a-z0-9]+$
          | | | | | End with any alphanumeric
          | | | |
          | | | Optional underscore in middle
          | | |
          | | Start with any alphanumeric
          | |
          | Any accepted chars
          | between 3 and 20 chars.
          |
          Start of string


          The positive lookahead ensures the length will be of minimum 3 chars and maximum 20, and we check for an optional underscore in between one or more characters.



          Try it here – I have added unit testing similar to yours as well.









          share|improve this answer




























            4












            4








            4







            Could be like this:



            ^(?=^w{3,20}$)[a-z0-9]+_?[a-z0-9]+$
            | | | | | End with any alphanumeric
            | | | |
            | | | Optional underscore in middle
            | | |
            | | Start with any alphanumeric
            | |
            | Any accepted chars
            | between 3 and 20 chars.
            |
            Start of string


            The positive lookahead ensures the length will be of minimum 3 chars and maximum 20, and we check for an optional underscore in between one or more characters.



            Try it here – I have added unit testing similar to yours as well.









            share|improve this answer















            Could be like this:



            ^(?=^w{3,20}$)[a-z0-9]+_?[a-z0-9]+$
            | | | | | End with any alphanumeric
            | | | |
            | | | Optional underscore in middle
            | | |
            | | Start with any alphanumeric
            | |
            | Any accepted chars
            | between 3 and 20 chars.
            |
            Start of string


            The positive lookahead ensures the length will be of minimum 3 chars and maximum 20, and we check for an optional underscore in between one or more characters.



            Try it here – I have added unit testing similar to yours as well.










            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Jan 27 at 20:07

























            answered Jan 27 at 19:29









            SiavasSiavas

            2,08611224




            2,08611224























                0














                If you consider using JS functions along with regex this is how i will do.



                In matches i included all the matching strings by neglecting the _ condition and in final i am checking for the _ condition.






                let str = `_vila
                v_v
                v__v
                v_v
                vvvvvv_
                12345678912345678912
                12345678912345678912123456`


                let matches = str.match(/^[a-z0-9]w{1,18}[a-z0-9]$/gm)

                let final = matches.map(e=> (e.split('_').length < 3 ? ({value:e,match:true}) : ({value:e,match:false})))

                console.log(final)








                share|improve this answer




























                  0














                  If you consider using JS functions along with regex this is how i will do.



                  In matches i included all the matching strings by neglecting the _ condition and in final i am checking for the _ condition.






                  let str = `_vila
                  v_v
                  v__v
                  v_v
                  vvvvvv_
                  12345678912345678912
                  12345678912345678912123456`


                  let matches = str.match(/^[a-z0-9]w{1,18}[a-z0-9]$/gm)

                  let final = matches.map(e=> (e.split('_').length < 3 ? ({value:e,match:true}) : ({value:e,match:false})))

                  console.log(final)








                  share|improve this answer


























                    0












                    0








                    0







                    If you consider using JS functions along with regex this is how i will do.



                    In matches i included all the matching strings by neglecting the _ condition and in final i am checking for the _ condition.






                    let str = `_vila
                    v_v
                    v__v
                    v_v
                    vvvvvv_
                    12345678912345678912
                    12345678912345678912123456`


                    let matches = str.match(/^[a-z0-9]w{1,18}[a-z0-9]$/gm)

                    let final = matches.map(e=> (e.split('_').length < 3 ? ({value:e,match:true}) : ({value:e,match:false})))

                    console.log(final)








                    share|improve this answer













                    If you consider using JS functions along with regex this is how i will do.



                    In matches i included all the matching strings by neglecting the _ condition and in final i am checking for the _ condition.






                    let str = `_vila
                    v_v
                    v__v
                    v_v
                    vvvvvv_
                    12345678912345678912
                    12345678912345678912123456`


                    let matches = str.match(/^[a-z0-9]w{1,18}[a-z0-9]$/gm)

                    let final = matches.map(e=> (e.split('_').length < 3 ? ({value:e,match:true}) : ({value:e,match:false})))

                    console.log(final)








                    let str = `_vila
                    v_v
                    v__v
                    v_v
                    vvvvvv_
                    12345678912345678912
                    12345678912345678912123456`


                    let matches = str.match(/^[a-z0-9]w{1,18}[a-z0-9]$/gm)

                    let final = matches.map(e=> (e.split('_').length < 3 ? ({value:e,match:true}) : ({value:e,match:false})))

                    console.log(final)





                    let str = `_vila
                    v_v
                    v__v
                    v_v
                    vvvvvv_
                    12345678912345678912
                    12345678912345678912123456`


                    let matches = str.match(/^[a-z0-9]w{1,18}[a-z0-9]$/gm)

                    let final = matches.map(e=> (e.split('_').length < 3 ? ({value:e,match:true}) : ({value:e,match:false})))

                    console.log(final)






                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Jan 27 at 19:25









                    Code ManiacCode Maniac

                    10.7k2733




                    10.7k2733























                        0














                        One more variant: /^[a-z0-9](?:[a-z0-9]|_(?!.*_)){1,18}[a-z0-9]$/i






                        share|improve this answer




























                          0














                          One more variant: /^[a-z0-9](?:[a-z0-9]|_(?!.*_)){1,18}[a-z0-9]$/i






                          share|improve this answer


























                            0












                            0








                            0







                            One more variant: /^[a-z0-9](?:[a-z0-9]|_(?!.*_)){1,18}[a-z0-9]$/i






                            share|improve this answer













                            One more variant: /^[a-z0-9](?:[a-z0-9]|_(?!.*_)){1,18}[a-z0-9]$/i







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Jan 27 at 19:39









                            vsemozhetbytvsemozhetbyt

                            2,213711




                            2,213711






























                                draft saved

                                draft discarded




















































                                Thanks for contributing an answer to Stack Overflow!


                                • Please be sure to answer the question. Provide details and share your research!

                                But avoid



                                • Asking for help, clarification, or responding to other answers.

                                • Making statements based on opinion; back them up with references or personal experience.


                                To learn more, see our tips on writing great answers.




                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function () {
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54391861%2fregexp-for-matching-usernames-min-3-chars-max-20-chars-optional-underscore-in%23new-answer', 'question_page');
                                }
                                );

                                Post as a guest















                                Required, but never shown





















































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown

































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown







                                Popular posts from this blog

                                MongoDB - Not Authorized To Execute Command

                                How to fix TextFormField cause rebuild widget in Flutter

                                in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith