tags:

views:

119

answers:

5

I want to validate usernames according to this schema:

  1. Allowable characters: letters, numbers, hyphen, underscore
  2. First character must be a letter or a number
  3. The username cannot be all numbers

This regular expression satisfies 1 and 2 above, but I can't figure out how to satisfy 3:

/^[a-zA-Z\d][\w\-]+$/

(I'm using Ruby, if that's relevant)

+8  A: 

Not very efficient, but simple:

/^(?!\d+$)[a-zA-Z\d][\w\-]+$/

The lookahead simply means: "what follows isn't a string of numbers that go on until the end".

Max Shawabkeh
neat trick, is it available in non-ruby regex implementations?
Mark E
+1 Nice one. I usually avoid lookahead's because I don't grasp them very well, but this is a nice illustration of how to use them.
fireeyedboy
@Mark: Not in all, but in the most. Python, PHP, Perl and Javascript are ones I know support it.
Max Shawabkeh
+1  A: 

Not ideal, but easy: Use a second pass with the regex /^.*[a-zA-Z_\-].*$/

Just ensure it passes both and you'll be fine.

David Kanarek
`/\D/` would do just as well if you run a second filter. There's no need to match from start to end.
Max Shawabkeh
The second pass can just be `[-a-z_]` (I assume you're doing a case-insensitive match; if not, then `[-A-Za-z_]`, of course. No need to worry about beginning, end or anything else -- if it passes the first one, just make sure there's a letter, underscore, or hyphen anywhere.
Jay
Thanks, I wasn't sure about Ruby, but Java only reports a match if the entire string matches. I assume \D is not digit?
David Kanarek
@David: Yup, non-digit. As for matching, most engines (including Java) provide a `match()` and `search()`/`find()` methods. The former tries to match the whole string, while the latter tries to match at any point.
Max Shawabkeh
This is why I love SO, I learn something new and useful everyday.
David Kanarek
Actually, if we're talking 2 passes, the first pass should be /^\d+$/. It would negate the need for a second pass if the uname is all digits. An aside /^(\d|\-)$/ might be better
Elizabeth Buckwalter
A: 

If you can go with two passes, a simpler and faster second pass regexp is:

/[^\d]/

This just matches anything that is not a number and it needs to match only one and it terminates early. You don't really need to be strict here because the first pass already rejects non-allowable characters.

slebetman
A: 

I would use the regex that you need for validation and then something like:

passwd.to_i.to_s.length != passwd.length

to verify that passwd is not a string of digits after it passes the primary validation.

D.Shawley
A: 

Yet another way, though it may not perform as well as Max's:

/^[a-z0-9][-\w]*[-_a-z][-\w]*$/i
outis