¿Cuál es la diferencia entre la función exec () de RegExp y la función match () de String?

122

Si ejecuto esto:

/([^\/]+)+/g.exec('/a/b/c/d');

Entiendo esto:

["a", "a"]

Pero si ejecuto esto:

'/a/b/c/d'.match(/([^\/]+)+/g);

Entonces obtengo el resultado esperado de esto:

["a", "b", "c", "d"]

¿Cual es la diferencia?

Justin Warkentin
fuente
4
recorre con execpara obtener todas las sub-selecciones.
zzzzBov
2
Tenga en cuenta que el segundo +no es necesario ya matchque devolverá todas las sub-expresiones. .execsolo devuelve uno cada vez, por lo que tampoco lo necesita +.
pimvdb
3
Además de eso, los cuantificadores anidados como las dos ventajas deben usarse con mucho cuidado porque conducen fácilmente a un retroceso catastrófico .
Marius Schulz
1
@MariusSchulz Gracias por el enlace. Eso me llevó a aprender sobre cuantificadores posesivos y agrupación atómica. Cosas muy bonitas de entender.
Justin Warkentin

Respuestas:

118

execcon una expresión regular global está destinado a ser utilizado en un bucle, ya que aún recuperará todas las subexpresiones coincidentes. Entonces:

var re = /[^\/]+/g;
var match;

while (match = re.exec('/a/b/c/d')) {
    // match is now the next match, in array form.
}

// No more matches.

String.match hace esto por usted y descarta los grupos capturados.

Ry-
fuente
39
Tengo algo que agregar a esta respuesta, no se debe colocar la expresión regular literal dentro de la condición while, ¡así while(match = /[^\/]+/g.exec('/a/b/c/d')o creará un bucle infinito! Como se indica claramente en MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
yeyo
7
@yeyo: Más específicamente, tiene que ser el mismo objeto de expresión regular. Un literal no logra eso.
Ry-
@ Ry- Creo que hay que tener en cuenta que este comportamiento se introdujo en ES5. Antes de ES5 new RegExp("pattern")y /pattern/significaba cosas diferentes.
Robert
75

Una imagen es mejor, ya sabes ...

re_once = /([a-z])([A-Z])/
re_glob = /([a-z])([A-Z])/g

st = "aAbBcC"

console.log("match once="+ st.match(re_once)+ "  match glob="+ st.match(re_glob))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))

¿Ver la diferencia?

Nota: Para resaltar, observe que los grupos capturados (por ejemplo: a, A) se devuelven después del patrón coincidente (por ejemplo: aA), no es solo el patrón coincidente.

georg
fuente
28

/regex/.exec()devuelve solo la primera coincidencia encontrada, mientras que las "string".match()devuelve todas si usa la gbandera en la expresión regular.

Vea aquí: ejecutivo , partido .

Alex Ciminian
fuente
23

Si su expresión regular es global y está capturando, debe usar exec. Match no devolverá todas tus capturas.

Match funciona muy bien cuando solo se combinan (no se capturan). Lo ejecutas una vez y te da una matriz de todas las coincidencias. (aunque si la expresión regular no es global, la coincidencia mostrará la coincidencia seguida de capturas)

Exec es lo que usa cuando está capturando, y cada vez que se ejecuta da la coincidencia, seguida de las capturas. (la coincidencia se comportará de manera que proporcione la coincidencia completa seguida de capturas, solo cuando la expresión regular no es global).

Otro uso con Exec es obtener el índice o la posición de una coincidencia. Cuando tenga una variable para su expresión regular, puede usar .lastIndex y obtener la posición de la coincidencia. Un objeto regex tiene .lastIndex, y el objeto regex es en lo que haces .exec. La coincidencia de puntos se realiza en una cadena y no podrá hacer el objeto de expresión regular dot lastIndex

Una cadena, tiene la función de coincidencia, a la que se le pasa una expresión regular. Y una expresión regular, tiene la función exec y se le pasa una cadena

ejecutivo que ejecuta varias veces. partido corres una vez

Es bueno usar la coincidencia cuando no se captura y al capturar puede usar exec, que es más poderoso, ya que es bueno para obtener capturas, pero si usó la coincidencia al capturar, vea que muestra capturas cuando la expresión regular no es global, pero no muestra capturas cuando la expresión regular es global.

> "azb".match(/a(z)b/);
[ "azb", "z" ]

> "azb".match(/a(z)b/g);
[ "azb" ]
>

Otra cosa es que si usa exec, tenga en cuenta que se llama en la expresión regular, entonces si usó una variable para la expresión regular, tiene más poder

No obtienes las coincidencias cuando no usas la variable para la expresión regular, así que usa la variable para la expresión regular, cuando usas exec

> /./g.exec("abc")
[ "a" ]
> /./g.exec("abc")
[ "a" ]
> /./g.exec("abc")
[ "a" ]
>
> /[a-c]/g.exec("abc")
[ "a" ]
> /[a-c]/g.exec("abc")
[ "a" ]
>

> var r=/[a-c]/g
> r.exec("abc")
[ "a" ]
> r.exec("abc")
[ "b" ]
> r.exec("abc")
[ "c" ]
> r.exec("abc")
null
>

Y con el ejecutivo, puede obtener el "índice" de la coincidencia.

> var r=/T/g
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
2
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
6
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
9
> r.exec("qTqqqTqqTq");
null
> r.lastIndex
0
>

Entonces, si desea índices o captura, use exec (tenga en cuenta que, como puede ver, con el "índice", el "índice" que da es realmente una enésima aparición, cuenta desde 1. Por lo tanto, podría derivar el index al restar 1. Y como puede ver, da 0 - lastIndex de 0 - para no encontrado).

Y si desea estirar la coincidencia, puede usarla cuando está capturando, pero no cuando la expresión regular es global, y cuando lo hace para eso, entonces el contenido de la matriz no es todas las coincidencias, pero es el completo partido seguido de las capturas.

barlop
fuente
Sí, comprender el funcionamiento de r.lastIndexes el factor clave para comprender la diferencia entre execy match.
ejecuta
@barlop "La coincidencia no coincidirá con todas las capturas", ¿en serio? "a, b, c, aa, bb, cc" .match (/ (\ w +) / g) => ["a", "b", "c", "aa", "bb", "cc" ]. ¿Cómo explicar que los haya almacenado en caché todos?
MrHIDEn
@barlop If your regex is global, and you are capturing, then you must use exec. Match won't return all your captures.lo tengo en la consola. Simplemente copie / pegue "a,b,c,aa,bb,cc".match(/(\w+)/g);Opera, Firefox.
MrHIDEn
@MrHIDEn No usarías el lenguaje que usaste en tu cita incorrecta. Y lo que importa es lo que se muestra y lo que podemos ver ... si hay almacenamiento en caché detrás de escena tampoco es relevante. Y ha pasado un tiempo desde que miré esto, pero no muestra todas las capturas. Incluso si haces tu ejemplo. "a,b,c,aa,bb,cc".match(/(\w+)/g) Lo que está sucediendo allí muestra todas las coincidencias, y da la casualidad de que capturaste todas las coincidencias, así que si mostrara todas las capturas, se vería exactamente igual (cntd)
barlop
(cntd) Entonces, tal vez esté pensando que está mostrando las capturas, pero no es así, está mostrando las coincidencias
barlop
6

El .match () la función str.match(regexp)hará lo siguiente:

  • si no es un partido que volverá:
    • si la gbandera se usa en la expresión regular: devolverá todas las subcadenas (ignorando los grupos de captura)
    • si la gbandera no se usa en la expresión regular: devolverá lo mismo queregexp.exec(str)
  • si no hay coincidencia, devolverá:
    • null

Ejemplos de .match () usando la gbandera:

var str = "qqqABApppabacccaba";
var e1, e2, e3, e4, e5;
e1 = str.match(/nop/g); //null
e2 = str.match(/no(p)/g); //null
e3 = str.match(/aba/g); //["aba", "aba"]
e4 = str.match(/aba/gi); //["ABA", "aba", "aba"]
e5 = str.match(/(ab)a/g); //["aba", "aba"] ignoring capture groups as it is using the g flag

Y .match () sin la gbandera es equivalente a .exec () :

e1=JSON.stringify(str.match(/nop/))===JSON.stringify(/nop/.exec(str)); //true
//e2 ... e4 //true
e5=JSON.stringify(str.match(/(ab)a/))===JSON.stringify(/(ab)a/.exec(str)); //true

El .exec () la función regexp.exec(str)hará lo siguiente:

  • si no es un partido que volverá:
    • si la gbandera se usa en la expresión regular: devolverá (por cada vez que se llame) : [N_MatchedStr, N_Captured1, N_Captured2, ...]de la próxima Ncoincidencia. Importante: no avanzará a la siguiente coincidencia si el objeto regexp no está almacenado en una variable (debe ser el mismo objeto)
    • si la gbandera no se usa en la expresión regular: devolverá lo mismo que si tuviera una gbandera y se llamara por primera vez y solo una vez.
  • si no hay coincidencia, devolverá:
    • null

Ejemplo de .exec () (regexp almacenado + usando la gbandera = cambia con cada llamada):

var str = "qqqABApppabacccaba";
var myexec, rgxp = /(ab)a/gi;

myexec = rgxp.exec(str);
console.log(myexec); //["ABA", "AB"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "ab"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "ab"]
myexec = rgxp.exec(str);
console.log(myexec); //null

//But in this case you should use a loop:
var mtch, myRe = /(ab)a/gi;
while(mtch = myRe.exec(str)){ //infinite looping with direct regexps: /(ab)a/gi.exec()
    console.log("elm: "+mtch[0]+" all: "+mtch+" indx: "+myRe.lastIndex);
    //1st iteration = elm: "ABA" all: ["ABA", "AB"] indx: 6
    //2nd iteration = elm: "aba" all: ["aba", "ab"] indx: 12
    //3rd iteration = elm: "aba" all: ["aba", "ab"] indx: 18
}

Ejemplos de .exec () cuando no cambia con cada llamada:

var str = "qqqABApppabacccaba", myexec, myexec2;

//doesn't go into the next one because no g flag
var rgxp = /(a)(ba)/;
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "a", "ba"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "a", "ba"]
//... ["aba", "a", "ba"]

//doesn't go into the next one because direct regexp
myexec2 = /(ab)a/gi.exec(str);
console.log(myexec2); //["ABA", "AB"]
myexec2 = /(ab)a/gi.exec(str);
console.log(myexec2); //["ABA", "AB"]
//... ["ABA", "AB"]
ajax333221
fuente
1

A veces, regex.exec () tardará mucho más tiempo que string.match ().

Vale la pena mencionar que si el resultado de string.match () y regex.exec () es el mismo (por ejemplo, cuando no se usa el indicador \ g), regex.exec () tomará entre x2 y x30 y luego string. partido():

Por lo tanto, en tales casos, el uso del enfoque de "new RegExp (). Exec ()" debe usarse solo cuando necesite una expresión regular global (es decir, para ejecutar más de una vez).

Dorony
fuente
1
¿Tiene un punto de referencia?
Sơn Trần-Nguyễn