Motores de regex comparados: JavaScript, Python, Java, PCRE y RE2
Qué funciones de regex funcionan en qué motor, probadas lado a lado: lookbehind, grupos con nombre, Unicode, grupos atómicos, recursión, qué hace $ antes de un salto de línea y qué motores pueden congelarse con un patrón malo.

· 6 min de lectura
/\$\s*(\d+)/
atrapa
- $40
- $ 40
- $5
deja libre
- 40
- USD 40
- $x
JavaScript, Python, Java y PCRE son todos motores de regex con backtracking y casi la misma sintaxis básica, así que la mayoría de los patrones del día a día funcionan en los cuatro. Se diferencian en los detalles que rompen el código cuando mueves un patrón: cuánto lookbehind permiten, cómo se escriben los grupos con nombre, si \d coincide con dígitos de otras escrituras y qué hace $ antes de un salto de línea final. RE2 (que usan Go, Google Sheets y BigQuery) es la excepción: renuncia a las referencias inversas y los lookarounds a cambio de garantizar que la búsqueda nunca tarde más que tiempo lineal.
Cada fila de abajo se probó el 4 de noviembre de 2026, no se copió de la documentación: Node 22.23 para JavaScript, Python 3.12.6 (re), Java 17.0.20 (java.util.regex), PCRE2 10.44 a través de PHP 8.5, y RE2 a través del paquete google-re2 1.1 de Google. Las versiones más nuevas pueden admitir más.
01Dónde te encontrarás cada motor
- JavaScript: todos los navegadores, Node, Deno y TypeScript. También es el motor detrás de los tableros de este sitio.
- Python: el módulo integrado
re. El módulo de tercerosregexañade mucho de lo que le falta are. - Java:
java.util.regex, que usan Kotlin y Scala en la JVM. - PCRE2: las funciones
preg_de PHP,grep -P, nginx y R conperl = TRUE. - RE2: la librería de Google. El paquete
regexpde Go sigue su sintaxis y su garantía de velocidad, igual queREGEXMATCHde Google Sheets yREGEXP_CONTAINSde BigQuery.
02La tabla de funciones
| JavaScript | Python re |
Java | PCRE2 | RE2 | |
|---|---|---|---|---|---|
Lookahead (?=…) |
Sí | Sí | Sí | Sí | No |
Lookbehind (?<=…) |
Sí, cualquier longitud | Solo ancho fijo | Sí, longitud acotada¹ | Sí, longitud acotada | No |
Referencias inversas \1 |
Sí | Sí | Sí | Sí | No |
| Grupo con nombre | (?<n>…) |
(?P<n>…) |
(?<n>…) |
Ambas | Ambas |
| Referencia inversa con nombre | \k<n> |
(?P=n) |
\k<n> |
Ambas | No |
Grupo atómico (?>…) |
No | Sí (3.11+) | Sí | Sí | No |
Posesivo a++ |
No | Sí (3.11+) | Sí | Sí | No |
Propiedad Unicode \p{L} |
Sí, con el modificador u |
No | Sí | Sí | Sí |
| Escritura, p. ej. griego | \p{Script=Greek} |
No | \p{IsGreek} |
\p{Greek} |
\p{Greek} |
\d coincide con ٣ (3 arábigo oriental) |
No | Sí | No | Solo en modo UCP² | No |
Recursión (?R) |
No | No | No | Sí | No |
Condicional (?(1)…|…) |
No | Sí | No | Sí | No |
Modificadores en línea (?i) |
No³ | Sí | Sí | Sí | Sí |
^abc$ coincide con "abc\n" |
No | Sí | Sí | Sí | No |
Fin del texto \z |
No | No, es \Z |
Sí | Sí | Sí |
| Puede congelarse con un patrón malo | Sí | Sí | Sí | Se detiene en un límite⁴ | No |
- La documentación de Java pide una longitud máxima acotada, pero Java 17 también aceptó
(?<=a+)en nuestra prueba. No confíes en ello. - Por defecto,
\den PCRE2 es ASCII. El modificador/ude PHP activa el modo UCP, por eso\dcoincidió con٣ahí. - Node 22 rechaza tanto
(?i)como la forma acotada(?i:…). La forma acotada es nueva en ES2025 y solo funciona en entornos de ejecución más recientes. - Mira la prueba de velocidad de abajo: PHP devolvió un error en lugar de una respuesta.
03Las diferencias que rompen los patrones que mueves
$ y un salto de línea final
^abc$ coincide con "abc\n" en Python, Java y PCRE, porque su $ también coincide justo antes de un salto de línea final. En JavaScript y RE2 no. Una línea leída de un archivo con su salto de línea todavía pegado pasa la validación en un lenguaje y falla en otro. Para decir “el final del todo”, usa \z en Java, PCRE y RE2, y \Z en Python (donde \Z es el final absoluto). JavaScript no tiene ninguno de los dos: sin el modificador m, su $ ya significa el final del todo.
Lookbehind
JavaScript permite cualquier lookbehind, incluidos los de longitud variable como (?<=\$\s*)\d+. El re de Python solo permite ancho fijo, PCRE2 una longitud acotada y RE2 ninguno. La solución portable es hacer coincidir el prefijo y capturar lo que quieres:
/\$\s*(\d+)/Debe coincidir2/2
- Total: $40
- Total: $ 40
No debe2/2
- Total: 40
- Total: USD 40
El grupo 1 contiene el número en todos los motores, RE2 incluido.
Grupos con nombre
(?<year>\d{4}) funciona en JavaScript, Java, PCRE2 y RE2, pero el re de Python 3.12 lo rechaza y pide (?P<year>\d{4}). Las referencias inversas a un nombre también cambian: \k<year> en JavaScript, Java y PCRE, (?P=year) en Python. Regex en JavaScript y en Python tiene la lista completa para esos dos.
\d, \w y Unicode
En Python 3, \d y \w coinciden por defecto con dígitos y letras de todas las escrituras, así que \d acepta ٣. En JavaScript, Java y RE2 son solo ASCII. Para un campo de formulario que debe ser 0-9, escribe [0-9] y el patrón significará lo mismo en todas partes (en Python también puedes pasar re.ASCII).
Para letras de cualquier idioma, \p{L} funciona en JavaScript (con el modificador u), Java, PCRE2 y RE2, pero no en el re de Python.
Grupos atómicos y cuantificadores posesivos
(?>a+) y a++ toman todo lo que pueden y nunca lo devuelven, lo que es una forma de frenar un backtracking desbocado. Java y PCRE los tienen desde hace años, y Python los añadió en la 3.11. JavaScript y RE2 no los tienen; en JavaScript, un lookahead con una referencia inversa, (?=(a+))\1, hace el mismo trabajo.
04Velocidad: qué motores pueden congelarse
El patrón malo clásico es (a+)+$ contra una larga serie de la letra a seguida de !. Un motor con backtracking prueba todas las formas de repartir la serie entre el + interior y el exterior antes de rendirse. Esto es lo que hizo cada motor con 24 letras y un !:
| Motor | Resultado |
|---|---|
| JavaScript (Node 22) | 11,2 segundos |
| Python 3.12 | 1,8 segundos |
| Java 17 | Menos de 1 ms |
| PCRE2 (PHP) | Se rindió a los 3 ms con “Backtrack limit exhausted” |
| RE2 | Menos de 1 ms, incluso con 100.000 letras |
Destacan dos cosas. Java 17 optimiza esta forma, pero sigue siendo un motor con backtracking: añade una referencia inversa, como en ^(a+)+\1$, y su tiempo pasó de 9 ms a 45 ms y a 590 ms al crecer la entrada de 16 a 20 y a 24 letras. PHP no se cuelga, pero preg_match devuelve false, y el código que solo comprueba si hay una coincidencia verdadera lo interpreta como “sin coincidencia” sin notar el error.
Solo RE2 garantiza tiempo lineal para todos los patrones, por eso es la opción segura para patrones que escriben los usuarios. Backtracking catastrófico explica cómo detectar y corregir estos patrones en cualquier motor.
05Escribir un patrón que funcione en todas partes
Si un patrón tiene que ejecutarse en más de un motor, por ejemplo en el navegador y en el servidor, este subconjunto se comporta igual en los cinco:
- Texto literal, clases como
[a-z0-9_],.y los cuantificadores* + ? {n,m}con sus versiones perezosas. - Las anclas
^y$con el modificadormcuando te refieres a líneas, o\A…\zen el servidor cuando te refieres al texto completo (y nunca le pases un salto de línea final a un$de JavaScript). - Los grupos
(…)y(?:…), y la alternancia|. [0-9]en lugar de\d, si hay que rechazar dígitos de otras escrituras.- Ni lookarounds ni referencias inversas si RE2 puede ejecutarlo; captura la parte que quieres.
Después prueba el patrón en cada motor donde vaya a ejecutarse. La Zona de pruebas usa el motor de JavaScript, igual que esta página.
06Practica
Los packs de práctica y la caza del día usan el motor de JavaScript, así que el lookbehind y los grupos con nombre están permitidos. La Guía de regex cubre cada función con ejemplos interactivos.