It's always fascinating to see the responses this kind of question elicits.
The specs in the question aren't very clear, so I'll just assume the string can contain only ASCII letters and digits, with hyphens, underscores and spaces as internal separators. The meat of the problem is insuring that the first and last character are not separators, and that there's never more than one separator in a row (that part seems clear, anyway). Here's the simplest way:
/^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/
After matching one or more alphanumeric characters, if there's a separator it must be followed by one or more alphanumerics; repeat as needed.
Lars's regex:
/^[[:alnum:]]+(?:[-_ ]?[[:alnum:]]+)*$/
...is effectively the same (assuming your regex flavor supports the POSIX character-class notation), but why make the separator optional? The only reason you'd be in that part of the regex in the first place is if there's a separator or some other, invalid character.
On the other hand, PhantomCode's regex:
/^[a-zA-Z0-9]+([_\s\-]?[a-zA-Z0-9])*$/
...only works because the separator is optional. After the first separator, it can only match one alphanumeric at a time. To match more, it has to keep repeating the whole group: zero separators followed by one alphanumeric, over and over. If the second [a-zA-Z0-9]
were followed by a plus sign, it could find a match by a much more direct route.
Then there's Philippe's regex:
/^[a-zA-Z0-9][a-zA-Z0-9_\s\-]*[a-zA-Z0-9](?<![_\s\-]{2,}.*)$/
It can be made to work in flavors other than .NET by changing the lookbehind to a lookahead:
/^(?!.*[_\s-]{2,})[a-zA-Z0-9][a-zA-Z0-9_\s\-]*[a-zA-Z0-9]$/
...but it's still way more complicated than it needs to be.
Your own regex:
/^[a-zA-Z0-9]+([a-zA-Z0-9](_|-| )[a-zA-Z0-9])*[a-zA-Z0-9]+$/
...requires the string to start and end with two alphanumeric characters--I don't think that's what you want. Also, as @froh42 pointed out, if there are two separators within the string, there have to be exactly two alphanumerics between them--again, probably not what you wanted. And, as @ymv pointed out (with an assist from @Welbog), the (_|-| )
in your regex should be [-_ ]
. That part's not incorrect, but if you have a choice between an alternation and a character class, you should always go with the character class: they're much, much more efficient.
Again, I'm not worried about whether "alphanumeric" is supposed to include non-ASCII characters, or the exact meaning of "space", just how to enforce a policy of non-contiguous internal separators with a regex.