Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Regex to validate user names with at least one letter and no special characters

I'm trying to write a user name validation that has the following restrictions:

  1. Must contain at least 1 letter (a-zA-Z)
  2. May not contain anything other than digits, letters, or underscores

The following examples are valid: abc123, my_name, 12345a

The following examples are invalid: 123456, my_name!, _1235

I found something about using positive lookaheads for the letter contraint: (?=.*[a-zA-Z]), and it looks like there could be some sort of negative lookahead for the second constraint, but I'm not sure how to mix them together into one regex. (Note... I am not really clear on what the .* portion does inside the lookahead...)

Is it something like this: /(?=.*[a-zA-Z])(?!.*[^a-zA-Z0-9_])/

Edit:

Because the question asks for a regex, the answer I'm accepting is:

 /^[a-zA-Z0-9_]*[a-zA-Z][a-zA-Z0-9_]*$/

However, the thing I'm actually going to implement is the suggestion by Bryan Oakley to split it into multiple smaller checks. This makes it easier to both read and extend in the future in case requirements change. Thanks all!

And because I tagged this with ruby-on-rails, I'll include the code I'm actually using:

validate :username_format    

def username_format
  has_one_letter = username =~ /[a-zA-Z]/
  all_valid_characters = username =~ /^[a-zA-Z0-9_]+$/
  errors.add(:username, "must have at least one letter and contain only letters, digits, or underscores") unless (has_one_letter and all_valid_characters)
end
like image 233
Paul Avatar asked May 23 '11 18:05

Paul


2 Answers

/^[a-zA-Z0-9_]*[a-zA-Z][a-zA-Z0-9_]*$/: 0 or more valid characters followed by one alphabetical followed by 0 or more valid characters, constrained to be both the beginning and the end of the line.

like image 136
Shea Levy Avatar answered Sep 23 '22 18:09

Shea Levy


It's easy to check whether the pattern has any illegal characters, and it's easy to check whether there's at least one letter. Trying to do that all in one regular expression will make your code hard to understand.

My recommendation is to do two tests. Put the tests in functions to make your code absolutely dead-simple to understand:

if no_illegal_characters(string) && contains_one_alpha(string) {
    ...
}

For the former you can use the pattern ^[a-zA-Z0-9_]+$, and for the latter you can use [a-zA-Z].

If you don't like the extra functions that's ok, just don't try to solve the problem with one difficult-to-read regular expression. There are no bonus points awarded for cramming as much functionality into one expression as possible.

like image 40
Bryan Oakley Avatar answered Sep 22 '22 18:09

Bryan Oakley