
Coincidencia «greedy» frente a «lazy» en expresiones regulares: por qué tu patrón abarca demasiado
Publicado el 24 jul 2026
Escribes <b>.*</b> para extraer el contenido de una etiqueta de negrita, lo pruebas con <b>Hello</b> y funciona perfectamente. Luego lo ejecutas en código HTML real con dos etiquetas de negrita en la misma línea — <b>Hello</b> and <b>World</b> — y, en lugar de dos coincidencias, obtienes una coincidencia gigante que abarca ambas etiquetas. Esto no es un error de tu motor de expresiones regulares; es el comportamiento predeterminado de *, + y {n,m}, y se soluciona con un solo carácter una vez que sabes lo que está pasando.
Por qué .* captura más de lo que esperas
Por defecto, los cuantificadores en las expresiones regulares son codiciosos: .* no significa «coincidir con algunos caracteres», sino «coincidir con tantos caracteres como sea posible y, a continuación, retroceder solo si el resto del patrón lo requiere absolutamente».
Analicemos <b>.*</b> frente a <b>Hello</b> and <b>World</b> paso a paso:
<b>coincide con el primer<b>..*empieza consumiendo todo el resto de la cadena: todo hasta el final, incluido el segundo<b>World</b>.- A continuación, el motor necesita encontrar
</b>para completar la coincidencia, por lo que empieza a retroceder desde el final de.*un carácter cada vez. - El primer lugar (escaneando hacia atrás desde el final) donde encaja
</b>es el último</b>de la cadena —no el primero en el que «lógicamente» debería detenerse—.
Así pues, la coincidencia acaba siendo «<b>Hello</b> and <b>World</b>» en su totalidad, porque la coincidencia codiciosa siempre prueba primero la cadena más larga posible y solo la reduce lo mínimo necesario para que el resto del patrón coincida.
La solución: convertir el cuantificador en «perezoso» con ?
Añadir un ? inmediatamente después de un cuantificador lo cambia de «codicioso» a «perezoso» (también llamado «no codicioso» o «reacio»): *?, +?, ??, {n,m}?.
Un cuantificador perezoso hace lo contrario: empieza por coincidir el menor número posible de caracteres y solo se expande si el resto del patrón aún no puede completarse.
<b>.*?</b> aplicado a la misma cadena:
<b>coincide con el primer<b>..*?empieza coincidiendo con cero caracteres.- El motor comprueba: ¿coincide
</b>justo aquí? No (estamos en «Hello…», aún no en un</b>), por lo que expande.*?exactamente un carácter y vuelve a comprobar. - Esto se repite carácter a carácter hasta que
.*?haya consumido exactamenteHello, momento en el que</b>coincide inmediatamente.
Resultado: <b>Hello</b> y <b>World</b> aparecen como dos coincidencias separadas, que es casi siempre lo que realmente se desea al analizar estructuras similares a etiquetas o delimitadores.
Cuándo realmente quieres el comportamiento «greedy» (no es solo «el valor predeterminado incorrecto»)
El comportamiento «greedy» no es un error del lenguaje: es correcto para un caso diferente, igualmente común: coincidir con el límite más externo de algo, no con la unidad más pequeña en su interior. Si estás extrayendo «todo lo que hay entre el primer { y el último } » en un bloque de texto similar a JSON (por ejemplo, para obtener un objeto completo independientemente del anidamiento), el modo «greedy» es exactamente el adecuado, mientras que el modo «lazy» se detendría en el primer } interior, lo que te daría un fragmento truncado e inválido.
Regla general: «lazy» para fragmentos pequeños y delimitados que se repiten (etiquetas, cadenas entre comillas, elementos de lista); «greedy» para «capturar todo el intervalo exterior».
Referencia rápida
| Patrón | Comportamiento | Úsalo cuando |
|---|---|---|
.*, .+, {n,m} |
«Greedy» — coincide con el más largo posible | Quieres el span más externo, o solo hay una coincidencia en la cadena |
.*?, .+?, {n,m}? |
Perezoso: coincide con el fragmento más corto posible | Tienes varios fragmentos delimitados similares y quieres cada uno por separado |
Si no estás seguro de cuál de los dos comportamientos adopta realmente un patrón con tu entrada real —en lugar de con tu cadena de prueba—, ejecutarlo sobre el texto real con coincidencias múltiples en un probador de expresiones regulares con resaltado de coincidencias en tiempo real hace que la diferencia sea visible de inmediato: verás un bloque gigante resaltado en el caso del modo «greedy», frente a varios bloques separados en el modo «lazy», lo cual suele ser más rápido que analizarlo carácter por carácter.