Cuantificadores: Codiciosos (Greedy), Perezosos (Lazy) y Posesivos
Domina el control de repeticiones con *, +, ?, {n,m}. Aprende la diferencia crítica entre coincidencia codiciosa, perezosa (*?) y posesiva (*+).
¿Qué es un Cuantificador?
Un cuantificador especifica cuántas veces consecutivas debe repetirse el carácter, clase o grupo que le precede inmediatamente en el patrón.
| Cuantificador | Equivalente | Descripción |
|---|---|---|
* | {0,} | Cero o más veces: Es opcional y puede repetirse indefinidamente. |
+ | {1,} | Una o más veces: Obligatorio al menos una vez. |
? | {0,1} | Cero o una vez: Elemento completamente opcional. |
{n} | {n,n} | Exactamente n repeticiones fijas. |
{min,} | {min,infinito} | Al menos min repeticiones en adelante. |
{min,max} | — | Entre min y max repeticiones inclusive. |
Los Tres Modos de Cuantificación en PCRE
1. Modo Codicioso (Greedy - Por Defecto)
Por defecto, todos los cuantificadores en PHP son codiciosos: consumen tantos caracteres como sea posible hasta el final del texto, y si el resto del patrón no coincide, retroceden paso a paso (backtracking).
El clásico error al parsear HTML:
En el texto <b>Hola</b> y <b>Adiós</b>, el patrón /<b>.*<\/b>/ coincidirá con TODO desde el primer <b> hasta el último </b>, ¡abarcando ambos bloques en una sola coincidencia indeseada!
2. Modo Perezoso o Reacio (Lazy / Reluctant - Añadiendo ?)
Si agregas un signo de interrogación ? inmediatamente después de cualquier cuantificador (*?, +?, ??, {min,max}?), el cuantificador se vuelve perezoso: consume la menor cantidad posible de caracteres para satisfacer la coincidencia.
// Captura cada etiqueta en coincidencias individuales sin comerse el texto intermedio:
$patron = '/<b>.*?<\/b>/';
3. Modo Posesivo (Possessive - Añadiendo +)
Si agregas un signo de suma + inmediatamente después del cuantificador (*+, ++, ?+, {min,max}+), el cuantificador se vuelve posesivo: devora todo lo que puede y NUNCA retrocede (sin backtracking). Si la coincidencia falla más adelante, falla inmediatamente. Es una optimización clave de rendimiento y seguridad contra ReDoS.
⚡ Comparación en Vivo: Greedy vs Lazy al Extraer Etiquetas HTML
Ejemplo interactivo. Observa la enorme diferencia entre la extracción codiciosa (.*) y la extracción perezosa (.*?):
<?php
$html = "Noticias: <span>PHP 8.3 lanzado</span> y también <span>MySQL 8.4 LTS disponible</span> para servidores.";
// 1. Patrón Codicioso (Greedy): abarcará desde el primer <span> hasta el último </span>
$patronGreedy = '/<span>.*<\/span>/';
// 2. Patrón Perezoso (Lazy): se detendrá en el primer </span> que encuentre
$patronLazy = '/<span>.*?<\/span>/';
echo "<div style='font-family:system-ui; max-width:640px; padding:15px;'>";
echo "<h3 style='color:#0f172a; margin-top:0;'>⚡ Demostración Greedy vs Lazy</h3>";
echo "<p style='color:#64748b; font-size:13px;'>Texto original: <code>" . htmlspecialchars($html) . "</code></p>";
// Prueba 1: Greedy
preg_match($patronGreedy, $html, $matchesGreedy);
echo "<div style='background:#fef2f2; border:1px solid #fecaca; border-left:4px solid #ef4444; padding:12px; border-radius:6px; margin-bottom:12px;'>";
echo " <strong style='color:#991b1b;'>1. Codicioso / Greedy (/<span>.*<\/span>/):</strong><br>";
echo " <span style='color:#64748b; font-size:12px;'>Devoró todo el texto intermedio entre la primera y la última etiqueta:</span><br>";
echo " <code style='color:#b91c1c; font-weight:bold; background:#fff; padding:3px 6px; border-radius:4px; display:inline-block; margin-top:4px;'>" . htmlspecialchars($matchesGreedy[0]) . "</code>";
echo "</div>";
// Prueba 2: Lazy
preg_match_all($patronLazy, $html, $matchesLazy);
echo "<div style='background:#ecfdf5; border:1px solid #a7f3d0; border-left:4px solid #10b981; padding:12px; border-radius:6px;'>";
echo " <strong style='color:#065f46;'>2. Perezoso / Lazy (/<span>.*?<\/span>/):</strong><br>";
echo " <span style='color:#64748b; font-size:12px;'>Aisló correctamente cada etiqueta de forma independiente (" . count($matchesLazy[0]) . " resultados):</span><br>";
foreach ($matchesLazy[0] as $i => $coincidencia) {
echo " <div style='margin-top:4px;'><span style='color:#059669; font-weight:bold;'>[" . ($i+1) . "]</span> <code style='background:#fff; padding:2px 6px; border-radius:4px; color:#047857;'>" . htmlspecialchars($coincidencia) . "</code></div>";
}
echo "</div>";
echo "</div>";
Mini-Quiz de Retención
Comprueba tu comprensión antes de pasar a la siguiente lección¿Qué símbolo se coloca inmediatamente después de un cuantificador (como * o +) para convertirlo en perezoso (lazy)?
¿Tienes dudas con este tema?
Sócrates AI puede darte analogías y explicarte este código paso a paso.🚀 ¿Quieres dominar Regex en PHP: Expresiones Regulares & PCRE y crear proyectos reales?
Aprende arquitectura limpia, desarrollo web moderno, buenas prácticas y despliegue en producción con nuestro Bootcamp Fullstack Developer con mentorías en vivo.
🏛️ Autoría y Propiedad Intelectual: Obra educativa original de David Amador para Kilopolis Academia (Solucionáticos), estructurada sobre las especificaciones y estándares abiertos del W3C, el estándar vivo de WHATWG y la documentación oficial de código abierto. © Todos los derechos reservados.