tags:

views:

73

answers:

3

Hi, just a quick question am abit rubish with regex so thought I would post on here. The regex below is to validate a username.

  • Must be between 4-26 characters long

  • Start with atleast 2 letters

  • Can only contain numbers and one underscore and one dot

I have this so far, but isn't working

<?php

$username=$_POST['username'];

if (!eregi("^([a-zA-Z][0-9_.]){4,26}$",$username))
{
 return false;
}
else
{
 echo "username ok";
}

?>

Thanks :)

A: 

The part where you said:

and one underscore and one dot

would steer me away from regular expressions to be honest!

I would do something like:

if(!eregi('^[a-zA-Z]{2}$', substr(0,2))) {
    // .... check for only 2 dots here later ....
}
Kevin Sedgley
haha why.......?
Elliott
Simply because it would be far far easier to write a some `for/else` code at this point. I'm sure there is a regex for it but it'll be complex!
Kevin Sedgley
Good point, thanks for your feedback :)
Elliott
+3  A: 

You are asking for a lot in one regex, this may be possible but it is not the best way, you want to let the user know why their username is erroring out.

function checkUsername($username) {
    $username = trim($username);
    if (empty($username)) {
        return "username was left blank.";
    }elseif (strlen($username) < 4) {
        return "username was too short";
    }elseif (strlen($username) > 26) {
        return "username was too long";
    }elseif (!preg_match('~^[a-z]{2}~i', $username)) {
        return "username must start with two letters";
    }elseif (preg_match('~[^a-z0-9_.]+~i', $username)) {
        return "username contains invalid characters.";
    }elseif (substr_count($username, ".") > 1) {
        return "username may only contain one or less periods.";
    }elseif (substr_count($username, "_") > 1) {
        return "username may only contain one or less underscores.";
    }

    return true;
} 

Then to check you would do:

$validUsername = checkUsername($username);

if ($validusername !== true) {
     echo "An error occured: " . $validUsername;
}

This way the user knows there was an error and can fix it easier instead of being blind to what the error is. Also you should stop using ereg it has been depreciated use preg_match instead.

NOTE the use of the !== to check, this also checks type since it can return a string (which would return true with a loose comparison). Just an FYI.

Brad F Jacobs
Thanks, it only echo'd "username ok" as that was sent back to the jquery function. I did have a list of the requirements the username needs, but I guess your way is better. I will give it ago :)
Elliott
Added the 2 starting letters requirement.
Brad F Jacobs
`preg_match('~[a-z0-9_.]+~i', $username)` will return `1` for `foo@bar` as `[a-z0-9_.]+` matches `foo`.
Gumbo
Thanks for catching that, added the start and ending anchors to fix it.
Brad F Jacobs
I tested it, works fine but the underscore and periods dont work if I have one. Also I can have special characters such as '# [ ] ` etc
Elliott
Yea, I edited it accordingly.
Brad F Jacobs
Instead of exploding and counting you could also use `substr_count()` (http://php.net/substr_count).
Geert
@Geert, good call. Modified accordingly.
Brad F Jacobs
+3  A: 

You could use the regex

/^(?=[a-z]{2})(?=.{4,26})(?=[^.]*\.?[^.]*$)(?=[^_]*_?[^_]*$)[\w.]+$/iD

as in

<?php

$username=$_POST['username'];

if (!preg_match('/^(?=[a-z]{2})(?=.{4,26})(?=[^.]*\.?[^.]*$)(?=[^_]*_?[^_]*$)[\w.]+$/iD',
                $username))
{
 return false;
}
else
{
 echo "username ok";
}

?>
  • The ^(?=[a-z]{2}) ensure the string "Start with atleast 2 letters".
  • The (?=.{4,26}) ensure it "Must be between 4-26 characters long".
  • The (?=[^.]*\.?[^.]*$) ensures the following characters contains at most one . until the end.
  • Similarly (?=[^_]*_?[^_]*$) ensures at most one _.
  • The [\w.]+$ commits the match. It also ensures only alphanumerics, _ and . will be involved.

(Note: this regex assumes hello_world is a valid user name.)

KennyTM
Make it start with more that two letters and it will be perfect :) "Start with **atleast** 2 letters".
Colin Hebert
@Colin: Should be fixed.
KennyTM
But at the sametime it can only contains numbers, so the last chars must be only "\d._" and there can't be any number in the first part. Here is one based on your previous regex :
Colin Hebert
/^(?=.{4,26})(?=[a-z]{2,}[^a-z]*$)(?=[^.]*\.?[^.]*$)(?=[^.]*\.?[^.]*$)(?=[^_]*_?[^_]*$)[\w._]*$/i - Each lookhead is in the order of the asked contraints.
Colin Hebert
i have made: `^(([a-z]{2})([a-z0-9]{2,24}))$` and works great apart from i cant get the single dot to work. anyone wish to extend go ahead.
RobertPitt
@RobertPitt you don't respect the "only numbers" part neither.
Colin Hebert
Numbers are taken into consideration with `([a-z0-9]{2,24})` just the `_` and `.` for single
RobertPitt
@RobertPitt, "az9az0za9z" is valid. Even if you accept the numbers, there is not only numbers at the end.
Colin Hebert
Do not forget to add the `/D` modifier. This is important because `$` will allow a final newline in the string, e.g. `"eviluser\n"`.
Geert
@Geert: Thanks. Did not know there's such a modifier.
KennyTM