¿Cuál es la diferencia entre corchetes y paréntesis en una expresión regular?

101

Aquí hay una expresión regular que creé para usar en JavaScript:

var reg_num = /^(7|8|9)\d{9}$/

Aquí hay otro sugerido por mi miembro de equipo.

var reg_num = /^[7|8|9][\d]{9}$/

La regla es validar un número de teléfono:

  • Debe tener solo diez números.
  • Se supone que el primer número es 7, 8 o 9.
Jayapal Chandran
fuente

Respuestas:

124

Estas expresiones regulares son equivalentes (para fines de coincidencia):

  • /^(7|8|9)\d{9}$/
  • /^[789]\d{9}$/
  • /^[7-9]\d{9}$/

La explicación:

  • (a|b|c)es una expresión regular "OR" y significa "aob o c", aunque la presencia de corchetes, necesaria para OR, también captura el dígito. Para ser estrictamente equivalente, codificaría (?:7|8|9)para convertirlo en un grupo que no captura.

  • [abc]es una "clase de caracteres" que significa "cualquier carácter de a, bo c" (una clase de caracteres puede usar rangos, p. ej. [a-d]= [abcd])

La razón por la que estas expresiones regulares son similares es que una clase de caracteres es una forma abreviada de "o" (pero solo para caracteres individuales). Alternativamente, también puede hacer algo como (abc|def)que no se traduce en una clase de carácter.

Bohemio
fuente
30
(7|8|9)y [789]no son equivalentes, porque el primero captura, el segundo no. (?:7|8|9)sería equivalente por otro lado (supongo que lo sabes por supuesto ...).
hochl
Estoy viendo esta expresión regular: [<<|>>|\]\]|\[\[]. Debido al contexto, sé que regex está tratando de coincidir con <<o >>o [[o ]]. Pero por lo que ha dicho, debería coincidir con <o >o [o ]. Si usa |between [], ¿los corchetes se comportan de manera diferente?
Daniel Kaplan
1
@DanielKaplan no lo use |dentro de una clase de carácter [...], a menos que desee hacer coincidir el carácter de tubería en sí. Además, la duplicación de caracteres en una clase de carácter no tiene ningún efecto: una clase de carácter es una lista de caracteres y coincidirá exactamente con uno de ellos. Supongo que quiere un grupo , que usa paréntesis redondos normales:(<<|>>|\]\]|\[\[)
Bohemio
57

El consejo de su equipo es casi correcto, excepto por el error que se cometió. Una vez que descubras por qué, nunca lo olvidarás. Eche un vistazo a este error.

/^(7|8|9)\d{9}$/

Que hace esto:

  • ^y $denota coincidencias ancladas, lo que afirma que el subpatrón entre estos anclajes es la coincidencia completa. La cadena solo coincidirá si el subpatrón coincide con la totalidad, no solo con una sección.
  • ()denota un grupo de captura .
  • 7|8|9denota búsqueda de cualquiera de 7, 8o 9. Lo hace con alternancias , que es lo que hace el operador de tuberías |, alternando entre alternancias. Esto retrocede entre alternancias: si la primera alternancia no coincide, el motor tiene que regresar antes de que la ubicación del puntero se mueva durante la coincidencia de la alternancia, para continuar haciendo coincidir la siguiente alternancia; Mientras que la clase de personaje puede avanzar secuencialmente. Vea esta coincidencia en un motor de expresiones regulares con optimizaciones deshabilitadas:
Pattern: (r|f)at
Match string: carat

alternancias

Pattern: [rf]at
Match string: carat

clase

  • \d{9}coincide con nueve dígitos. \des un metacarácter abreviado, que coincide con cualquier dígito.
/^[7|8|9][\d]{9}$/

Mira lo que hace:

  • ^y también $denota fósforos anclados.
  • [7|8|9]es una clase de personaje . Los caracteres de la lista 7, |, 8, |, o 9se pueden combinar, por lo tanto el |se añadió en forma incorrecta. Esto coincide sin retroceder.
  • [\d]es una clase de personaje que habita en el metacarácter \d. La combinación del uso de una clase de carácter y un solo metacarácter es una mala idea, por cierto, ya que la capa de abstracción puede ralentizar la coincidencia, pero esto es solo un detalle de implementación y solo se aplica a algunas implementaciones de expresiones regulares. JavaScript no es uno, pero hace que el subpatrón sea un poco más largo.
  • {9} indica que la construcción única anterior se repite nueve veces en total.

La expresión regular óptima es /^[789]\d{9}$/, porque /^(7|8|9)\d{9}$/captura innecesariamente, lo que impone una disminución del rendimiento en la mayoría de las implementaciones de expresiones regulares (resulta ser uno, considerando que la pregunta usa palabras clave varen el código, probablemente sea JavaScript). El uso deque se ejecuta en PCRE para la coincidencia de preg optimizará la falta de retroceso, sin embargo, tampoco estamos en PHP, por lo que usar clases en []lugar de alternancias |brinda una bonificación de rendimiento ya que la coincidencia no retrocede y, por lo tanto, coincide y falla más rápido que usar su expresión regular anterior.

Uniedro
fuente
6
solo por interés, ¿de qué programa es esa captura de pantalla?
Mr Mystery Guest
12

Los primeros 2 ejemplos actúan de manera muy diferente si los está REEMPLAZANDO por algo. Si coincide en esto:

str = str.replace(/^(7|8|9)/ig,''); 

reemplazaría 7, 8 o 9 por la cadena vacía.

Si coincide en esto

str = str.replace(/^[7|8|9]/ig,''); 

reemplazarás 7o 8o 9O LA BARRA VERTICAL !!!! por la cadena vacía.

Descubrí esto por las malas.

Sheila
fuente
6
¡Bienvenido a SO! Reemplazar o combinar, es simplemente incorrecto. Mucha gente comete ese error y, por lo general, se sale con la suya, a veces durante años, porque sus cadenas de entrada nunca contienen una tubería ( |).
Alan Moore