Las especificaciones en la pregunta no son muy claras, así que supongo que la cadena puede contener solo letras y dígitos ASCII, con guiones, guiones bajos y espacios como separadores internos. La esencia del problema es asegurar que el primer y el último personaje no sean separadores, y que nunca haya más de un separador en una fila (esa parte parece clara, de todos modos). Esta es la forma más sencilla:
/^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/
Después de comparar uno o más caracteres alfanuméricos, si hay un separador, se debe ser seguido por uno o más caracteres alfanuméricos; repita según sea necesario.
Echemos un vistazo a las expresiones regulares de algunas de las otras respuestas.
/^[[:alnum:]]+(?:[-_ ]?[[:alnum:]]+)*$/
Esto es efectivamente lo mismo (asumiendo que su sabor es compatible con la notación de expresiones regulares de clase de caracteres POSIX), pero ¿por qué hacer el separador opcional? La única razón por la que estarías en esa parte de la expresión regular en primer lugar es si hay un separador u otro personaje no válido.
/^[a-zA-Z0-9]+([_\s\-]?[a-zA-Z0-9])*$/
Por otro lado, esto sólo funciona porque el separador es opcional. Después del primer separador, solo puede coincidir con un alfanumérico a la vez. Para que coincida más, tiene que seguir repitiendo todo el grupo: separadores de cero seguidos de un alfanumérico, una y otra vez. Si el segundo [a-zA-Z0-9]
fuera seguido por un signo más, podría encontrar una coincidencia por una ruta mucho más directa.
/^[a-zA-Z0-9][a-zA-Z0-9_\s\-]*[a-zA-Z0-9](?<![_\s\-]{2,}.*)$/
Este utiliza de búsqueda hacia atrás sin límites, que es una característica muy rara, pero se puede utilizar una búsqueda hacia delante en el mismo sentido:
/^(?!.*[_\s-]{2,})[a-zA-Z0-9][a-zA-Z0-9_\s\-]*[a-zA-Z0-9]$/
Esto realiza esencialmente una búsqueda separada por dos separadores consecutivos, y falla el partido si encuentra uno. El cuerpo principal solo necesita asegurarse de que todos los caracteres sean alfanuméricos o separadores, y que el primero y el último sean alfanuméricos. Dado que esos dos son obligatorios, el nombre debe tener al menos dos caracteres de longitud.
/^[a-zA-Z0-9]+([a-zA-Z0-9](_|-|)[a-zA-Z0-9])*[a-zA-Z0-9]+$/
Ésta es su propia expresión regular, y requiere la cuerda para empezar y terminar con dos caracteres alfanuméricos, y si hay dos separadores dentro de la cadena, no tiene por qué ser exactamente dos caracteres alfanuméricos entre ellos. Así que ab
, ab-cd
y ab-cd-ef
coincidirán, pero a
, a-b
y a-b-c
no lo harán.
Además, como han señalado algunos de los comentaristas, el (_|-|)
en su expresión regular debería ser [-_ ]
. Esa parte no es incorrecta, pero si puede elegir entre una alternancia y una clase de caracteres, siempre debe ir con la clase de caracteres: son más eficientes y más legibles.
De nuevo, no me preocupa si se supone que "alfanumérico" debe incluir caracteres no ASCII, o el significado exacto de "espacio", cómo aplicar una política de separadores internos no contiguos con una expresión regular.
Esto no permitirá caracteres no latinos en los nombres de usuario. Si desea poder manejar caracteres no latinos, debe usar una clase de caracteres incorporada en lugar de definir explícitamente qué caracteres son letras y números. – Welbog
Puede probar su expresión en línea: http://www.gskinner.com/RegExr/ – twk
'/^[a-zA-Z0-9] + ([a-zA-Z0-9] (_ | - |) [a-zA-Z0-9]) * [a-zA-Z0-9] * $/'es suficiente. No es necesario agregar '+' en el último –