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
backtrackingpara 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 »