Optimización y Casos Avanzados en Expresiones Regulares
Vamos a ver técnicas avanzadas como lookaheads, lookbehinds y cuantificadores perezosos, además de cómo optimizar patrones. También se incluye una referencia de metacaracteres.
Lookaheads y Lookbehinds
Estas técnicas permiten hacer coincidencias condicionadas:
- Lookahead
(?=...)
: Coincide si un patrón sigue al actual, sin incluirlo en el resultado. - Lookbehind
(?<=...)
: Coincide si un patrón precede al actual, sin incluirlo.
<?php
// Asegúrate de usar PHP ≥ 7.3 para lookaheads completos
$texto = 'Precio: $100 USD';
$patron = '/\$\d+(?=\sUSD)/'; // Busca $ seguido de dígitos solo
// si justo después hay un espacio y "USD"
if (preg_match($patron, $texto, $match)) {
echo "¡Coincidencia!: " . $match[0] . "\\n"; // -> ¡Coincidencia!: $100\n
} else {
echo "Nada encontrado\\n";
}
?>
Nota: Si por algún motivo no coincide, verifica que el espacio entre el número y "USD" sea un espacio estándar (ASCII 0x20) y que tu versión de PHP soporte lookaheads.
<?php
$texto = 'Precio: $100 USD'; // comillas simples
$patron = '/(?<=Precio: )\$\d+/';
// Coincide: "$100" precedido por "Precio: "
// No coincide: "$100" sin "Precio: "
preg_match($patron, $texto, $match);
print_r($match); // [0] => $100
?>
Cuantificadores perezosos
Por defecto, los cuantificadores como *
y +
son codiciosos: capturan todo lo posible. Si se añade ?
, se vuelven perezosos y capturan lo mínimo necesario.
<?php
$texto = "Link: http://abc.com y http://blah.com";
$patron = "/http:\/\/.*\.(com|net|org)/";
// Codicioso: Coincide desde "http://" hasta el último ".com"
echo preg_replace($patron, "<b>\\0</b>", $texto) . "\n"; // escapando el \0
$patron = "/http:\/\/.*?\.(com|net|org)/";
// Perezoso: Coincide con cada URL individual
echo preg_replace($patron, "<b>\\0</b>", $texto) . "\n"; // escapando el \0
// Link: http://abc.com y http://blah.com
// Link: http://abc.com y http://blah.com
?>
Recomendación general para preg_replace() en PHP
- Usa \\0 para toda la coincidencia
- Usa \\1, \\2, etc. para los grupos capturados
- Si usas comillas dobles, escapa las barras.
Optimización
- Evita patrones como
.*.*
, que generan backtracking excesivo. - Usa anclas como
^
y$
para limitar coincidencias al inicio o final de cadena. - Prefiere rangos específicos como
[a-z]
antes que.
(cualquier carácter).
Ejemplo complejo:
Este patrón ilustra múltiples conceptos juntos (comentado paso a paso debajo):
<?php
$texto = "ab12gh_a";
$patron = "/^.{2}[a‐z]{1,2}_?[0‐9]*([1‐6]|[a‐f])[^1‐9]{2}a+$/";
preg_match($patron, $texto, $match);
print_r($match);
?>
Desglose del patrón:
^
— Inicio de cadena.{2}
— Cualquier dos caracteres[a-z]{1,2}
— Una o dos letras minúsculas_?
— Guion bajo opcional[0-9]*
— Cero o más dígitos([1-6]|[a-f])
— Un dígito 1-6 o letra a-f[^1-9]{2}
— Dos caracteres que no sean del 1 al 9a+
— Una o más letras "a"$
— Final de cadena
Referencia de Metacaracteres
^
: Inicio de cadena$
: Fin de cadena.
: Cualquier carácter excepto nueva línea*
: 0 o más repeticiones+
: 1 o más repeticiones?
: 0 o 1 repetición (o vuelve un cuantificador perezoso){n}
: Exactamente n repeticiones{n,}
: Al menos n repeticiones{n,m}
: Entre n y m repeticiones[]
: Rango de caracteres|
: Alternancia()
: Agrupación y captura\b
: Límite de palabra\d
: Dígito\w
: Carácter alfanumérico\s
: Espacio en blanco
Seguridad
- Evita entradas demasiado largas o con patrones maliciosos
- Controla el
backtracking
para evitar bloqueos - En
php.ini
, puedes limitarpcre.backtrack_limit
<?php
$texto = str_repeat("a", 1000000);
$patron = "/(a+)+$/";
try {
preg_match($patron, $texto);
} catch (Exception $e) {
echo "Error de backtracking\n"; // si supera el límite pcre.backtrack_limit
//en php.ini
}
?>
Conclusión
Estas técnicas avanzadas permiten construir expresiones regulares más eficientes y precisas. Aunque no siempre son necesarias, conocerlas ayuda a mejorar el rendimiento y legibilidad del código cuando trabajas con texto complejo.
- Anterior « Capturas y reemplazos
- Siguiente Fin del tema »