La mayoría de las tardes perdidas con regex se deben a uno de tres errores. Un cuantificador es voraz y toma más de lo que querías. Un punto se niega a cruzar un salto de línea. O un reemplazo solo toca la primera coincidencia porque la bandera g está apagada. Ninguno lanza un error. Cada uno devuelve un resultado que parece correcto en el único ejemplo que probaste, y eso es lo que los vuelve caros: no sabes que algo falla hasta que el patrón se encuentra con datos reales.
¿Por qué mi patrón coincide con mucho más de lo que quería?
Porque * y + son voraces. Toman todos los caracteres que pueden y los devuelven de a uno solo hasta que el resto del patrón logra coincidir. Si ejecutas <.*> sobre <b>hi</b> and <i>yo</i> obtienes una sola coincidencia: la cadena entera, desde el primer < hasta el último >. El patrón hizo lo que escribiste. Escribiste "cualquier tramo de caracteres entre un corchete y otro corchete", y el tramo más largo posible es todo.
Hay dos soluciones y no son lo mismo. Un signo de interrogación después del cuantificador lo vuelve perezoso: <.*?> se detiene en el primer > y devuelve cuatro coincidencias (<b>, </b>, <i>, </i>). Una clase negada dice lo que de verdad quieres decir: <[^>]*> es "un corchete, luego cualquier cosa que no sea un corchete de cierre, luego un corchete de cierre". Da las mismas cuatro coincidencias con este texto. Además es el hábito más seguro, porque un punto perezoso sigue cruzando cualquier carácter que necesite, y una clase negada no puede.
La señal es una cantidad de coincidencias menor a la esperada, con una que es sospechosamente larga. Pega tu texto en el probador de regex y mira el resaltado: un único bloque de color que atraviesa varias de las cosas que querías por separado es una coincidencia voraz, siempre.
¿Por qué el patrón se frena al final de una línea?
Porque el punto coincide con cualquier carácter excepto un salto de línea. Toma tres líneas, Name: Ada, Role: Admin, Name: Grace, y el patrón Name:.*Admin. No encuentra nada. El texto está ahí, pero el punto no puede saltar el salto de línea entre la primera línea y la segunda.
Este es el que más tiempo cuesta porque el patrón funcionó cuando lo probaste en una sola línea. Pegaste una línea en la caja, obtuviste una coincidencia y llevaste el patrón a un código que lee un archivo entero. La causa habitual de "funciona en el probador y no devuelve nada en producción" es esta, o la bandera de más abajo.
Hay dos salidas. La bandera s deja que el punto coincida con un salto de línea; el probador de regex tiene una casilla para eso. O escribe [\s\S], que significa "espacio en blanco o no espacio en blanco" y por tanto cualquier carácter. La herramienta de buscar y reemplazar no tiene una opción s, así que ahí lo que funciona es [\s\S].
Arreglarlo puede causar el primer error. Activa s y un Name: (.*) voraz ya no se detiene al final de su línea: devuelve una sola coincidencia que va desde el primer Name: hasta el final del texto, llevándose las otras dos líneas. Con el punto confinado a una línea, la voracidad era inofensiva. Con el punto suelto, no lo es, y los cuantificadores perezosos o las clases negadas pasan a ser necesarios en el mismo momento.
Una trampa relacionada son ^ y $. Sin la bandera m significan el principio y el final de todo el texto, así que ^Role no coincide con nada en medio de un archivo. Las dos herramientas de aquí se comportan distinto: el probador arranca con m apagada, mientras que la herramienta de buscar y reemplazar arranca con "^ y $ coinciden en cada línea" encendida. Un patrón copiado de una a la otra puede cambiar de significado sin editar nada.
¿Por qué solo se reemplazó la primera coincidencia?
Porque la bandera g está apagada. Sin ella el motor se detiene después de la primera coincidencia, y el reemplazo se detiene con él. En JavaScript, reemplazar - por + en a-b-c da a+b-c sin g y a+b+c con ella.
La trampa es que nada parece roto. En una prueba de una línea el resultado es correcto. En un documento de dos mil líneas se arregla la primera aparición y las otras 1.999 quedan intactas, y a menos que busques después el texto viejo no lo vas a ver. Cuando el reemplazo falla por exactamente "todas menos una", mira primero las banderas.
La misma bandera tiene un segundo efecto en el código. Un objeto regex creado con g recuerda dónde terminó su última coincidencia, en una propiedad llamada lastIndex. Llamar a .test() dos veces con la misma cadena devuelve true y luego false, porque la segunda búsqueda empieza después de la primera coincidencia. Si una comprobación pasa con algunas entradas y falla con las mismas entradas dentro de un bucle, una regex /g compartida es una causa probable. Quita la bandera cuando solo necesites un sí o un no. El probador de este sitio construye un patrón nuevo con cada pulsación de tecla, así que no puede reproducir esto; solo aparece en código que reutiliza uno.
¿Cómo distingo cuál de los errores tengo delante?
Cuenta antes de leer. Calcula cuántas coincidencias esperas y compáralo con el número que informa la herramienta. La diferencia apunta a la causa:
- Menos coincidencias de las esperadas, una de ellas muy larga: cuantificador voraz.
- Cero o menos coincidencias, en un texto que abarca varias líneas: el punto, o
^y$sinm. - La cantidad correcta de coincidencias, pero el reemplazo solo cambió una:
gestá apagada. - La cantidad es correcta y el texto claramente está ahí, pero nada coincide: algo del texto no es lo que parece. Un espacio de no separación o una comilla curva derrota a cualquier patrón, y conviene descartar los caracteres invisibles en un texto antes de reescribir la regex.
Después reduce el problema. Recorta el texto hasta la muestra más pequeña que siga fallando, y recorta el patrón hasta la parte que falla, agregando piezas de a una. Una regex que falla entera suele tener una pieza que coincidió con algo inesperado, y solo ves cuál construyendo desde cero. Leer un patrón largo buscando el error es más lento que reconstruirlo.
Cuando hay un reemplazo de por medio, revisa también la salida y no solo las coincidencias. Los errores al buscar y reemplazar explica cómo confirmar qué cambió en realidad, porque una lista de coincidencias correcta puede producir un resultado erróneo si la cadena de reemplazo contiene un $.
¿Qué cosas no detecta el probador?
Unos cuantos límites, para que no confíes en un resultado en verde más allá de lo que alcanza.
- Ejecuta el motor de JavaScript de tu navegador y nada más. El lookbehind, los grupos con nombre y los escapes de propiedad difieren entre JavaScript, Python, PCRE y Go, así que un patrón que pasa aquí puede fallar en otro lenguaje. Prueba en el lenguaje en el que vas a publicar.
- Prueba los primeros 50.000 caracteres del texto que pegues y lista las primeras 50 coincidencias. Un problema que solo aparece en la página 80 de un log largo no se va a ver.
- No puede detener un patrón catastrófico. Los cuantificadores anidados como
(a+)+$sobre la entrada equivocada pueden congelar la pestaña, y nada puede interrumpir una expresión regular una vez que empezó. El tope de 50.000 caracteres hace que la mayoría de los patrones malos terminen, y es una mitigación, no una garantía. - Te muestra qué hace tu patrón, no si es la herramienta correcta. Analizar HTML con una regex falla con el siguiente archivo aunque funcione con este; las entidades HTML son una de las razones por las que el texto no se parece a lo que buscaste. El
DOMParserincorporado en el navegador es mejor camino.
El patrón, el texto y el reemplazo los maneja tu navegador. No se sube nada, y eso importa aquí porque la gente prueba patrones con extractos de logs y registros de clientes.
Para ver los tres errores por ti mismo, pega los ejemplos de arriba en el probador de regex y alterna las casillas g, s y m. El resaltado y el conteo de coincidencias cambian al hacer clic, y eso enseña la diferencia más rápido que una descripción.
Si el patrón es correcto y el resultado sigue mal, es probable que el texto tenga caracteres que no son los que ves. Los caracteres invisibles en un texto explica cómo encontrarlos.
Preguntas frecuentes
¿Por qué mi regex coincide con más texto del que esperaba?
Los cuantificadores como * y + son voraces por defecto: toman todos los caracteres que pueden y los devuelven solo hasta que el resto del patrón coincide. Agregar un signo de interrogación vuelve perezoso al cuantificador, así que <.*?> se detiene en el primer corchete de cierre. Una clase negada como <[^>]*> suele ser más segura porque no puede cruzar el carácter que estás buscando.
¿Por qué el punto de mi regex no coincide con un salto de línea?
En JavaScript el punto coincide con cualquier carácter excepto un salto de línea, así que un patrón que funciona en una línea puede no devolver nada en un párrafo. Activa la bandera s para que el punto cruce líneas, o escribe [\s\S] en lugar del punto. La bandera m es un ajuste distinto: cambia lo que significan ^ y $ y no toca el punto.
¿Por qué mi regex reemplaza solo la primera coincidencia?
Porque la bandera g está apagada. Sin ella el motor se detiene en la primera coincidencia y el reemplazo se detiene con él, así que en un texto largo se cambia una aparición y las demás quedan intactas. Activa la bandera g para reemplazar todas las apariciones.
¿Por qué mi regex devuelve true y luego false con la misma cadena?
Un objeto regex con la bandera g guarda en lastIndex dónde terminó su última coincidencia, y la siguiente llamada empieza desde ahí. Por eso llamar a test() dos veces con la misma cadena devuelve true y luego false. Quita la bandera g cuando solo necesites una respuesta de sí o no, o crea una regex nueva para cada comprobación.
¿Por qué mi patrón funciona en el probador pero no en mi código?
Las causas habituales son un motor de regex distinto, una bandera que falta o un texto diferente del que pegaste. El lookbehind, los grupos con nombre y los escapes de propiedad varían entre JavaScript, Python, PCRE y Go. Los datos reales también suelen tener saltos de línea, espacios de no separación y comillas curvas que una prueba de una sola línea no tenía.
Última actualización 19 de octubre de 2026