Tema 5 · Cuantificadores

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 (.*?):

greedy_vs_lazy.php
Solo lectura
<?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 (/&lt;span&gt;.*&lt;\/span&gt;/):</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 (/&lt;span&gt;.*?&lt;\/span&gt;/):</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>";
Pruébalo tú mismo » Se abre en una nueva pestaña con ejecución en vivo
🧠
Mini-Quiz de Retención
Comprueba tu comprensión antes de pasar a la siguiente lección
1 Pregunta
Pregunta 1
¿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.