Apuntes php

PHP: Porque a veces necesitas un poco de caos en tu vida.

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 9
  • a+ — 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 limitar pcre.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.

TOP