RegExp.exec() devuelve NULL esporádicamente

6 minutos de lectura

avatar de usuario
paquete

Realmente me estoy volviendo loco por esto y ya he pasado una cantidad desproporcionada de tiempo tratando de averiguar qué está pasando aquí. Así que por favor dame una mano =)

Necesito hacer algunas coincidencias RegExp de cadenas en JavaScript. Desafortunadamente se comporta de manera muy extraña. Este código:

var rx = /(cat|dog)/gi;
var w = new Array("I have a cat and a dog too.", "There once was a dog and a cat.", "I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.");

for (var i in w) {
    var m = null;
    m = rx.exec(w[i]);
    if(m){
        document.writeln("<pre>" + i + "\nINPUT: " + w[i] + "\nMATCHES: " + m.slice(1) + "</pre>");
    }else{
        document.writeln("<pre>" + i + "\n'" + w[i] + "' FAILED.</pre>");
    }
}

Devuelve “gato” y “perro” para los dos primeros elementos, como debería ser, pero luego algunos exec()-las llamadas comienzan a regresar null. no entiendo porque

publiqué un violín aquídonde puede ejecutar y editar el código.

Y hasta ahora he probado esto en Chrome y Firefox.

  • falla solo en un "I have a cat and a dog too."parece

    – Fantasma silencioso

    18 de enero de 2011 a las 13:48

  • exec devuelve nulo si una coincidencia falla por diseño, por lo que, por alguna razón, no coincide.

    – Martín Jespersen

    18 de enero de 2011 a las 13:49

Oh aqui está. Debido a que está definiendo su expresión regular global, coincide primero caty en el segundo paso del ciclo dog. Entonces, básicamente solo necesita restablecer su expresión regular (su puntero interno) también. Cf. este:

var w = new Array("I have a cat and a dog too.", "I have a cat and a dog too.", "I have a cat and a dog too.", "I have a cat and a dog too.");

for (var i in w) {
    var rx = /(cat|dog)/gi;
    var m = null;
    m = rx.exec(w[i]);
    if(m){
        document.writeln("<p>" + i + "<br/>INPUT: " + w[i] + "<br/>MATCHES: " + w[i].length + "</p>");
    }else{
        document.writeln("<p><b>" + i + "<br/>'" + w[i] + "' FAILED.</b><br/>" + w[i].length + "</p>");
    }
    document.writeln(m);
}

  • Woh- “puntero interno de una expresión regular”? ¿Podría recomendar un recurso sobre eso? ¡Gracias!

    – kater louis

    14/01/2019 a las 15:00

  • Guau… He estado escribiendo JavaScript intensamente durante los últimos 14 años, y RexExps más y más intensamente durante los últimos 8 años, y esto me sorprende bastante. ¿Tendría una mejor comprensión de esto si fuera mejor en Perl?

    – Cody

    20 de octubre de 2021 a las 2:14

  • Te daría 1000 votos si pudiera. Eso me ahorró horas y me dejó boquiabierto.

    – Steinroe

    1 abr a las 8:47

El objeto regex tiene una propiedad lastIndex que se actualiza cuando se ejecuta exec. Entonces, cuando ejecuta la expresión regular en, por ejemplo, “Tengo un gato y un perro también”, lastIndex está establecido en 12. La próxima vez que ejecute exec en el mismo objeto regex, comienza a buscar desde el índice 12. Por lo tanto, debe restablecer el lastIndex propiedad entre cada corrida.

  • ¡Gracias por la explicación! Ayuda mucho configurando myRe.lastIndex = 0; para uso posterior.

    – Antonio

    20 de enero de 2013 a las 1:52

  • Creo que esta debería ser la respuesta correcta porque sigue la mejor práctica de reutilizar el mismo objeto regex

    – Pitufo

    18 de marzo de 2019 a las 20:17


  • De acuerdo, esta debería ser la respuesta correcta. Reutiliza el mismo objeto regex y también explica la mecánica interna. OP debería considerar cambiar.

    – Sean Coley

    16 dic 2019 a las 16:30

Dos cosas:

  1. La mencionada necesidad de Reiniciar al usar el g bandera (mundial). Para resolver esto, recomiendo simplemente asignar 0 hacia lastIndex miembro de RegExp objeto. Esto tiene un mejor rendimiento que destruir y recrear.
  2. Ten cuidado cuando usar in palabra clave para caminar un Array objeto, porque puede dar lugar a resultados inesperados con algunas librerías. A veces deberías consultar con algo como isNaN(i)o si sabe que no tiene agujeros, use el bucle for clásico.

El código puede ser:

var rx = /(cat|dog)/gi;
w = ["I have a cat and a dog too.", "There once was a dog and a cat.", "I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat."];

for (var i in w)
 if(!isNaN(i))        // Optional, check it is an element if Array could have some odd members.
  {
   var m = null;
   m = rx.exec(w[i]); // Run
   rx.lastIndex = 0;  // Reset
   if(m)
    {
     document.writeln("<pre>" + i + "\nINPUT: " + w[i] + "\nMATCHES: " + m.slice(1) + "</pre>");
    } else {
     document.writeln("<pre>" + i + "\n'" + w[i] + "' FAILED.</pre>");
    }
  }

  • Esta debería ser la respuesta correcta. Ajuste rx.lastIndex = 0 es mucho mejor que volver a crear el objeto RegEx dentro del ciclo.

    – Minoru

    25 sep 2019 a las 19:27

  • Aún así, sería mejor simplemente no usar el g marca cuando no lo quieras. No tiene sentido crear una expresión regular que actualice específicamente lastIndex solo para restablecerlo después de cada ejecución.

    – Roberto

    8 de agosto de 2021 a las 19:44


  • Es posible que desee buscar globalmente en un elemento y luego restablecer y reutilizar la expresión regular en el siguiente. Creo que el código OP es solo un ejemplo para mostrar lo que no entiende.

    – ESL

    3 sep 2021 a las 19:16

Tuve un problema similar usando solo /g, y la solución propuesta aquí no funcionó para mí en FireFox 3.6.8. Tengo mi script trabajando con

var myRegex = new RegExp("my string", "g");

Estoy agregando esto en caso de que alguien más tenga el mismo problema que tuve con la solución anterior.

¿Ha sido útil esta solución?