Qué son los lookarounds, en cristiano
La mayoría de regex hace match contra un trozo de texto y consume la parte que coincidió. Los lookarounds son distintos. Miran alrededor de la posición actual para confirmar una condición, pero no consumen texto.
Piensa en ellos como condiciones que puedes pegarle al match. “Empareja un número, pero solo si va precedido por un signo de dólar”. “Empareja una palabra, pero solo si no va seguida de un interrogante”. El signo de dólar y el interrogante se quedan donde están, el match empieza y termina alrededor de ellos.
Hay cuatro variantes, dos mirando hacia delante y dos hacia atrás, cada una con versión positiva y negativa.
Referencia rápida
| Sintaxis | Nombre | Uso |
|---|---|---|
(?=...) | Lookahead positivo | Empareja si va seguido del patrón |
(?!...) | Lookahead negativo | Empareja si no va seguido del patrón |
(?<=...) | Lookbehind positivo | Empareja si va precedido del patrón |
(?<!...) | Lookbehind negativo | Empareja si no va precedido del patrón |
| Ancho variable | Longitud de lookbehind | Solo motores modernos (JS, .NET, PCRE2) |
Los cuatro patrones de lookaround
Lookahead positivo (?=...): empareja si lo que viene después encaja.
"42 dolares".match(/\d+(?= dolares)/); // ["42"]
"42 euros".match(/\d+(?= dolares)/); // null
El match es 42. El espacio y dolares no están en el resultado, son solo confirmación.
Lookahead negativo (?!...): empareja si lo que viene después NO encaja.
"foo bar".match(/foo(?! bar)/); // null
"foo baz".match(/foo(?! bar)/); // ["foo"]
Lookbehind positivo (?<=...): empareja si lo que viene antes encaja.
"precio: 42".match(/(?<=precio: )\d+/); // ["42"]
"42".match(/(?<=precio: )\d+/); // null
Lookbehind negativo (?<!...): empareja si lo que viene antes NO encaja.
"abc 42".match(/(?<!\d)\d+/); // ["42"]
"3 42".match(/(?<!\d)\d+/); // ["3"] (el 42 falla el lookbehind)
Cuándo te salvan los lookarounds
Tres patrones aparecen constantemente:
Emparejar un valor precedido por una etiqueta:
"nombre: María, edad: 32".match(/(?<=edad: )\d+/); // ["32"]
Sin lookbehind, capturarías edad: 32 y luego quitarías la etiqueta. Lookbehind hace el match limpio.
Validar contraseñas con varias restricciones:
const password = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$/;
Cada lookahead comprueba una regla distinta (una minúscula, una mayúscula, un dígito, un símbolo). Sin lookaheads escribirías cuatro regex separadas y las combinarías en código.
Reemplazar algo solo en un contexto:
"a, b, c, d".replace(/(?<=, )\b\w/g, m => m.toUpperCase());
// "a, B, C, D"
Capitaliza letras que vienen después de , . Sin lookbehind, la coma y el espacio formarían parte del match y el reemplazo tendría que volver a añadirlos.
Lookbehind de ancho variable, la feature moderna
Hasta hace poco, la mayoría de motores regex pedían que el lookbehind fuera de longitud fija. Podías escribir (?<=abc)X pero no (?<=a.*c)X. La razón eran las tripas del motor: necesitaba saber cuánto retroceder.
En 2026, el panorama está mayormente arreglado:
- JavaScript (V8, JavaScriptCore, SpiderMonkey): lookbehind de ancho variable soportado desde 2018. Úsalo libremente.
- Python: solo ancho fijo. Usa el paquete externo
regexpara ancho variable. - PCRE2: ancho variable desde la versión 10.
- .NET: ancho variable soportado.
- Java: ancho variable con alternancia finita desde Java 13, totalmente variable desde Java 17.
- Go (
regexp/RE2): nada de lookbehind. RE2 prohíbe backreferences y lookarounds para garantizar tiempo lineal. - Rust (
regex): nada de lookbehind. Mismo linaje que RE2. - Ripgrep /
rg: nada de lookbehind por defecto. Pasa el flag PCRE2 para activarlo.
Si necesitas lookbehind en un lenguaje sin soporte, el workaround es un grupo de captura más una sustitución que descarte el prefijo. Es más feo pero portable.
// JS lookbehind ancho variable
"123abc456def".match(/(?<=[a-z]+)\d+/g);
// ["456"]
Si necesitas testear una regex sin escribir código, pégala en el tester regex de AldeaCode. Ejecuta el patrón contra tu texto en el navegador, sin subida.
Las trampas
Backtracking catastrófico: los lookarounds dentro de patrones codiciosos pueden hacer que el motor pruebe caminos exponencialmente. El arreglo es anclar los lookarounds a caracteres específicos o usar cuantificadores posesivos (donde estén soportados).
Confusión atómica: un lookbehind empareja la posición, no el texto capturado. Si intentas extraer el contenido del lookbehind a una captura, no funcionará. Usa un grupo de captura real para el prefijo y deja que el resto del regex haga su trabajo.
Sorpresas de motor: un regex que funciona en JavaScript puede fallar en Python o Go. Siempre testea en el motor real en el que vas a correr. No escribas regex en Stack Overflow y asumas que portea limpio.
Un flujo de trabajo práctico
Cuando vayas a meter un lookaround, pregúntate:
- ¿Estoy intentando emparejar algo solo en un contexto específico? Lookahead o lookbehind.
- ¿Estoy intentando excluir algo basado en contexto? Lookahead o lookbehind negativo.
- ¿Mi motor soporta ancho variable si lo necesito? Comprueba antes de escribir.
- ¿Mi regex va a correr sobre input hostil? Ojo con el backtracking catastrófico.
El tester regex, el buscar y reemplazar para testear el patrón contra texto real, y el eliminar líneas que contengan para filtrar con patrones más simples, todos corren en tu navegador. Los lookarounds son una de esas features que convierten diez líneas de código en una regex. Usados con cuidado, son precisos. Usados sin cuidado, son la razón de que tu CI tarde 30 minutos.