Regex to allow only letter, number and underscores

J N
J N used Ask the Experts™
on
Hi,

im curious if it is possible to allow users to create a username with only letters numbers and underscores. the underscores cannot be back to back either

thanks in advance
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Most Valuable Expert 2011
Top Expert 2016

Commented:
Yes, it can be done.  It might be easier to use a couple of expressions instead of trying to do this all in one REGEX.
http://xkcd.com/1171/
Most Valuable Expert 2011
Top Expert 2016

Commented:
Please see http://iconoun.com/demo/temp_jaymenagy.php

<?php // demo/temp_jaymenagy.php
ini_set('display_errors', TRUE);
error_reporting(E_ALL);

// SEE http://www.experts-exchange.com/Programming/Languages/Scripting/PHP/Q_28491049.html

function validate_password($pwd)
{
    $rgx
    = '/'      // REGREX DELIMITER
    . '['      // CHARACTER CLASS
    . '^'      // NEGATION - MATCH ANYTHING THAT IS NOT ONE OF THIS CLASS
    . 'A-Z'    // LETTERS
    . '0-9'    // NUMBERS
    . '_'      // UNDERSCORE
    . ']'      // END CHARACTER CLASS
    . '/'      // REGREX DELIMITER
    . 'i'      // CASE-INSENSITIVE
    ;
    if (preg_match($rgx, $pwd)) return FALSE;
    if (strpos($pwd, '__') === FALSE) return TRUE;
    return FALSE;
}

$dat = array
( 'abc123'
, 'abc123*'
, 'abc_123'
, 'abc__123'
)
;

foreach ($dat as $pwd)
{
    echo PHP_EOL . "<br>$pwd ";
    $sig = validate_password($pwd);
    if ($sig) echo "OK";
        else echo "FAIL";
}

Open in new window

Ed. note: Changed line 21 to use === instead of == comparison.

Commented:
Here's a pure regexp solution, enforcing a minimum length too:
'/^(?!.*__).*\w{6}?$/im'

Open in new window

Replace 6 with how many characters you want your passwords to have.

PS @Ray: instead of [^A-Z0-9_] you can simply say \W

HTH,
Dan
JavaScript Best Practices

Save hours in development time and avoid common mistakes by learning the best practices to use for JavaScript.

J NUnicorn wrangler

Author

Commented:
Hi

it is possible to ensure that there are not only numbers?

in Ray's example '12352' would pass

Commented:
Yup. Lookaheads are your friends:
'/^(?!.*__)(?=.*[A-Z]).*\w{6}?$/im'

Open in new window

Most Valuable Expert 2011
Top Expert 2016

Commented:
Are there any other requirements you want to add to the question?  For example, some of the common requirements for passwords demand that they start with a letter and have at least one upper, one lower, one special, one number and contain no natural language words, etc.  The more detailed you can make the rule set, the more likely you will get the best answer.
J NUnicorn wrangler

Author

Commented:
Its not for a password its for usernames

i want to limit the ability to only accept particular charteres as i use - and .  to denote spaces in system processes.

i do not want a user name:
  - of only number
 - only underscores
  - number and underscores

i think having it start with letters is the best

ex :

 jaymenagy
jayme_nagy
j_nagy123

all are desirable

Commented:
'/^[A-Z](?!.*__).*\w{5}?$/im'

Open in new window

Starts with a letter, does not have consecutive _, accepts only letters, numbers and _, at least 6 characters.
J NUnicorn wrangler

Author

Commented:
using rays code

function validate_password($pwd)
{
    $rgx
    = '/^[A-Z](?!.*__).*\w{5}?$/im';    
    ;
    if (preg_match($rgx, $pwd)) return FALSE;
    if (strpos($pwd, '__') === FALSE) return TRUE;
    return FALSE;
}

$dat = array
( 'abc123'
, 'abc123*'
, 'abc_123'
, 'abc__123'
, '12553'
)
;

foreach ($dat as $pwd)
{
    echo PHP_EOL . "<br>$pwd ";
    $sig = validate_password($pwd);
    if ($sig) echo "OK";
        else echo "FAIL";
}

Open in new window


i get

<br>abc123 FAIL
<br>abc123* OK
<br>abc_123 FAIL
<br>abc__123 FAIL
<br>12553 OK
Commented:
Yup. The results are inverse. Ray's code tested for failures, my expression is used to test for valid values.
$dat = array
( 'abc123'
, 'abc123*'
, 'abc_123'
, 'abc__123'
, '12553'
)
;
$rgx = '/^[A-Z](?!.*__).*\w{5}?$/im';
foreach ($dat as $pwd)
{
    echo PHP_EOL . "<br>$pwd ";
     if (preg_match($rgx, $pwd)) echo "OK";
          else echo "FAIL";
}

Open in new window

Most Valuable Expert 2011
Top Expert 2016
Commented:
As I see programmers sometimes struggle to understand complex and advanced concepts like regular expressions, I am more and more inclined to write a bit more code with comments that explain my thinking.  Strategy: Comment the "why" not the "what."  This code sample seems to implement the current iteration of the expressed rules, and it's not intended to be a criticism or contradiction of Dan's take on the problem.  Regex is a language unto itself, and since it's almost 100% made up of punctuation, it is very difficult to explain regex in natural language.  So I lean in the direction of simplifying and deconstructing the problem into parts that are easy to explain and easy to test.

<?php // demo/temp_jaymenagy.php
ini_set('display_errors', TRUE);
error_reporting(E_ALL);

// SEE http://www.experts-exchange.com/Programming/Languages/Scripting/PHP/Q_28491049.html

/**
 * Canonical Rules:
 *
 * Starts with a letter
 * May have only letters, numbers and underscores
 * May not have consecutive underscores
 *
 * Side-Effect Rules
 *
 * May not start with an underscore
 * May not be all numbers
 * May not be only numbers and underscores
 */
function validate_password($pwd)
{
    // ISOLATE THE FIRST CHARACTER
    $pwd = trim($pwd);
    $pwd_start = substr($pwd,0,1);

    // STARTS WITH A LETTER
    $rgx_starts_letter
    = '/'          // REGEX DELIMITER
    . '[A-Z]'      // CHARACTER CLASS OF LETTERS
    . '/'          // REGEX DELIMITER
    . 'i';         // CASE-INSENSITIVE
    ;
    if (!preg_match($rgx_starts_letter, $pwd_start)) return FALSE;

    // NOTHING BUT LETTERS, NUMBERS, UNDERSCORES
    $rgx_bad_characters
    = '/'          // REGEX DELIMITER
    . '[^A-Z0-9_]' // CHARACTER CLASS OF EVERYTHING THAT IS NOT LETTER, NUMBER, UNDERSCORE
    . '/'          // REGEX DELIMITER
    . 'i';         // CASE-INSENSITIVE
    ;
    if (preg_match($rgx_bad_characters, $pwd)) return FALSE;

    // NO CONSECUTIVE UNDERSCORES
    if (strpos($pwd, '__') !== FALSE) return FALSE;

    // ALL TESTS PASSED
    return TRUE;
}

$dat = array
( 'abc123'
, 'abc123*'
, 'abc_123'
, 'abc__123'
, '123_abc'
, '123456'
, '1_2_3'
, 'a_1_2_3_'
, 'a_1_2_3__'
, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
)
;

foreach ($dat as $pwd)
{
    echo PHP_EOL . "<br>$pwd ";
    $sig = validate_password($pwd);
    if ($sig) echo "OK";
        else echo "FAIL";
}

Open in new window

J NUnicorn wrangler

Author

Commented:
Thanks guys !!!! big help

Regex is confusing as hell so i like the approach of taking it apart

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial