Skip to content
Back to Blog
regexjavascriptpythoncomparison

JavaScript vs Python Regex: Diferencias Clave

El mismo patrón puede coincidir en Python y fallar silenciosamente en JavaScript. Esta guía mapea las diferencias reales (anclas de cadena faltantes, soporte de lookbehind, sintaxis de reemplazo) para que puedas escribir regex portátiles.

SZ
Founder, Molixa
15 min read
Compartir
JavaScript vs Python Regex: Diferencias Clave
Table of contents12 sections

La mayor trampa en las expresiones regulares de JavaScript vs Python es que ambos motores comparten la mayor parte de la misma sintaxis, por lo que un patrón parece portátil hasta que se comporta mal silenciosamente. JavaScript usa el sabor ECMAScript; Python usa su propio módulo re, más cercano a PCRE. Coinciden en el 80% común, luego divergen en anclas, lookbehind, cadenas de reemplazo y Unicode. Esta guía tabula cada diferencia que realmente rompe el código, para que puedas escribir patrones que sobrevivan al copiar y pegar entre lenguajes.

Si alguna vez has tomado una expresión regular de Stack Overflow y la has visto coincidir en un entorno pero lanzar un error o no devolver nada en el otro, esta es la razón: la respuesta fue escrita para un motor. A continuación se muestra el mapa completo, con ejemplos y una forma rápida de probar el mismo patrón en ambos sabores antes de implementarlo.

JavaScript vs Python Regex de un Vistazo#

Aquí está la versión corta antes de la inmersión profunda. Estas son las diferencias que causan errores reales, no trivialidades.

CaracterísticaJavaScript (ECMAScript)Python (re)
Anclas de inicio/fin de cadenaNinguna (solo ^/$)\A y \Z
LookbehindFijo y variable (ES2018+)Solo ancho fijo
Cuantificadores posesivos (a++)No soportadoSoportado (Python 3.11+)
Grupos atómicos (?>...)No soportadoSoportado (Python 3.11+)
Sintaxis de grupo nombrado(?<name>...)(?P<name>...)
Referencia inversa nombrada\k<name>(?P=name)
Referencia de grupo en reemplazo$1, $<name>\1, \g<name>
Bandera . coincide con salto de líneas (dotall)re.DOTALL / re.S
Escapes de propiedad Unicode \p{...}Necesita bandera u o vNo incorporado (usar módulo regex)
Banderas en línea (?i)No soportadoSoportado
Manejo Unicode predeterminadoUnidades de código UTF-16Unicode por defecto en re

El resto de este artículo explica cada fila, por qué es importante y cómo reescribir un patrón para que funcione en ambos.

Consejo rápido: cuando un patrón funciona en un lenguaje y no en el otro, la causa casi siempre son los anclajes, lookbehind o la sintaxis de reemplazo. Revise esos tres primero antes de asumir que la expresión regular en sí está mal.

Anclajes: la brecha entre \A y \Z#

Este es el error más común que causa "funciona en Python, falla en JavaScript". Python admite dos conjuntos de anclajes, JavaScript solo uno.

En Python, ^ y $ coinciden al inicio y final de cada línea cuando la bandera re.MULTILINE está activa, mientras que \A y \Z siempre coinciden solo con el inicio y final absolutos de toda la cadena, independientemente de las banderas. Esta distinción permite escribir un validador estricto de cadena completa que ignora el comportamiento multilínea.

JavaScript no tiene \A ni \Z. Solo ofrece ^ y $, cuyo significado cambia según la bandera m (multilínea). Por lo tanto, un patrón de Python como \A\d{4}\Z no tiene un token equivalente directo en JavaScript.

// Python:    re.match(r"\A\d{4}\Z", text)
// Equivalente en JavaScript (sin bandera multilínea, así que ^ y $ son de cadena completa):
/^\d{4}$/.test(text);

La solución es omitir la bandera m en JavaScript y usar ^/$ simples, que entonces anclan toda la cadena. El inconveniente: $ en JavaScript también puede coincidir justo antes de un salto de línea final, así que para una estricta coincidencia de final de cadena usa $(?![\s\S]). Olvidar esto permite que una entrada multilínea pase un validador que parecía infalible.

Lookbehind: Ancho Fijo vs Ancho Variable#

Lookbehind es donde las dos variantes intercambian posiciones, lo que sorprende a quienes asumen que Python siempre es el motor más capaz.

  • JavaScript añadió lookbehind en ES2018 y admite tanto lookbehind de ancho fijo como de ancho variable. (?<=\$\d+) y (?<=cat|elephant) funcionan ambos.
  • El módulo re integrado de Python solo admite lookbehind de ancho fijo. (?<=cat|dog) funciona porque ambas alternativas tienen tres letras, pero (?<=cat|elephant) lanza re.error: look-behind requires fixed-width pattern.

Por lo tanto, un lookbehind de ancho variable que funciona bien en un navegador moderno generará un error en tiempo de compilación en Python. Si necesitas lookbehind variable en Python, instala el módulo de terceros regex, que lo admite, o reestructura el patrón para usar un grupo de captura en lugar de un lookbehind.

# Fallo en re de Python:
re.compile(r"(?<=USD |EUR )\d+")   # longitudes diferentes -> error
# Solución alternativa: capturar y referenciar, o rellenar alternativas al mismo ancho
re.compile(r"(?:USD|EUR) (\d+)")   # captura el número en el grupo 1

Si aún no tienes claro cómo se comportan realmente lookbehind y lookahead, vale la pena leer los mecanismos más profundos en nuestra guía de regex lookahead y lookbehind antes de portar patrones entre lenguajes.

Sintaxis de grupos de captura y referencias inversas#

Ambos motores admiten grupos con nombre, pero la sintaxis difiere, y esto es un error de compilación grave, no una discrepancia silenciosa.

JavaScript usa (?<name>...). Python usa (?P<name>...) con esa P adicional. Si los confundes, el patrón simplemente no compilará.

AcciónJavaScriptPython
Definir grupo con nombre(?<year>\d{4})(?P<year>\d{4})
Referencia inversa dentro del patrón\k<year>(?P=year) o \1
Referencia inversa numérica\1\1

Las referencias inversas numéricas (\1, \2) son idénticas en ambos, por lo que son la opción más segura cuando deseas que una misma cadena de patrón funcione sin cambios en ambos lenguajes. En el momento en que uses grupos con nombre, debes bifurcar tu patrón por motor.

Banderas en línea#

Python permite establecer banderas dentro del patrón con (?i) para insensibilidad a mayúsculas/minúsculas, (?m) para multilínea, etc., incluida la forma con alcance (?i:...). Históricamente, JavaScript no admitía modificadores de bandera en línea; las banderas solo iban en el literal (/pattern/i) o en el constructor RegExp. Los modificadores en línea con alcance (?i:...) son una adición reciente de ECMAScript y aún no son universales en todos los entornos de ejecución, por lo que no debes confiar en ellos para código portátil.

Sintaxis de la cadena de reemplazo (el error silencioso)#

Este es peligroso porque no lanza una excepción. Simplemente produce un resultado incorrecto, que es mucho más difícil de detectar en una revisión que un bloqueo.

Cuando haces referencia a un grupo capturado en una cadena de reemplazo, JavaScript usa el signo de dólar y Python usa una barra invertida.

// JavaScript: $1 referencia al grupo 1, $<name> referencia a un grupo con nombre
"2026-06-25".replace(/(\d{4})-(\d{2})/, "$2/$1");  // "06/2026-25"
# Python: \1 referencia al grupo 1, \g<name> referencia a un grupo con nombre
re.sub(r"(\d{4})-(\d{2})", r"\2/\1", "2026-06-25")  # "06/2026-25"

Si pegas una cadena de reemplazo de JavaScript con $1 en re.sub de Python, Python trata $1 como los caracteres literales dólar-uno, y tu reemplazo inserta silenciosamente $1 en la salida en lugar del valor capturado. Sin error, solo texto corrupto. Lo contrario también ocurre: un \1 en un reemplazo de JavaScript se lee como un escape y se rompe.

Advertencia: los errores de sintaxis en cadenas de reemplazo no generan excepciones. Siempre prueba una sustitución con una cadena de muestra real en el idioma de destino, no solo el patrón de coincidencia.

El indicador Dotall y la coincidencia multilínea#

Por defecto, . no coincide con un salto de línea en ninguno de los dos motores. Para cambiar eso, los nombres de los indicadores difieren.

  • JavaScript: el indicador s (introducido en ES2018), como en /foo.bar/s.
  • Python: re.DOTALL o su forma abreviada re.S, pasado a re.compile o a la llamada de función.

El comportamiento una vez habilitado es idéntico, pero JavaScript adjunta los indicadores como letras después de la barra de cierre o como el segundo argumento de RegExp, mientras que Python pasa constantes de indicador combinadas con el operador OR bit a bit, como re.DOTALL | re.IGNORECASE.

Un hábito portátil: evita confiar en . para cruzar líneas. Usa la clase explícita [\s\S] en cualquier lenguaje, que coincide con cualquier carácter, incluidos los saltos de línea, sin ningún indicador. Ese único truco elimina una de las discrepancias de indicadores más comunes entre lenguajes.

Unicode: Escapes de propiedades y comportamiento predeterminado#

Unicode es donde los motores divergen más silenciosamente, y afecta más a la entrada no ASCII.

JavaScript trata las cadenas como unidades de código UTF-16 de forma predeterminada. Sin el indicador u (Unicode), un carácter astral como un emoji son dos unidades de código, y . o un cuantificador pueden coincidir con la mitad, produciendo caracteres corruptos. Agregar el indicador u hace que la expresión regular opere en puntos de código y desbloquea escapes de propiedades Unicode como \p{Letter} y \p{Script=Greek}. El indicador v más nuevo extiende esto con operaciones de conjuntos.

Las cadenas de Python 3 son Unicode de forma predeterminada, por lo que \w y \d ya coinciden con letras y dígitos Unicode a menos que pases re.ASCII. Sin embargo, el módulo re incorporado de Python no admite escapes de propiedades \p{...} en absoluto. Si necesitas escapes de propiedades en Python, instalas el módulo de terceros regex, que los agrega.

NecesidadJavaScriptPython re
Coincidir por propiedad Unicode \p{L}Indicador u o vNo compatible (usa módulo regex)
Coincidencia correcta por punto de códigoRequiere indicador uPredeterminado
Caracteres de palabra solo ASCIIPredeterminado sin u es parcialIndicador re.ASCII

La conclusión práctica: en JavaScript, usa el indicador u siempre que tu entrada pueda contener texto no ASCII. En Python, espera \w/\d conscientes de Unicode de forma predeterminada, pero no uses \p{...} a menos que tengas instalada la biblioteca regex.

Cuantificadores posesivos y grupos atómicos#

Son características de control de retroceso. Los cuantificadores posesivos (a++, a*+, a?+) y los grupos atómicos ((?>...)) evitan que el motor retroceda dentro de una sección coincidente, lo que puede prevenir un retroceso catastrófico en entradas adversas. El re integrado de Python agregó ambos en Python 3.11. Las expresiones regulares de ECMAScript en JavaScript no los soportan.

Por lo tanto, un patrón que use (?>...) o a++ de un código base de Python 3.11+ genera un error de sintaxis en JavaScript. Para obtener un control similar en JavaScript, se reescribe el patrón con un truco de anticipación más referencia inversa (más difícil de leer) o se reestructura la expresión regular para que no sea ambigua desde el principio.

Cómo probar el mismo patrón en ambos sabores#

La forma fiable de evitar todas estas trampas es probar el patrón exacto con texto de muestra real en cada sabor antes de confirmarlo. Leer una tabla de compatibilidad te dice lo que debería pasar; ejecutar el patrón te dice lo que realmente pasa.

Paso 1: Escribe el patrón una vez y define tus cadenas de prueba#

Comienza con el patrón y de tres a cinco entradas de muestra que cubran tus casos límite: una coincidencia limpia, un casi acierto, un caso multilínea y un caso no ASCII si corresponde. Tenlos a la vista para poder confirmar las coincidencias visualmente.

Paso 2: Ejecútalo en el sabor de JavaScript#

Pega el patrón y las muestras en un probador de expresiones regulares multiformato gratuito configurado con el motor de JavaScript. Confirma las coincidencias, los grupos de captura y cualquier salida de reemplazo. Presta especial atención al comportamiento de los anclajes y al manejo de Unicode aquí.

Paso 3: Cambia el sabor a Python y vuelve a ejecutar#

Cambia el motor a Python y ejecuta el mismo patrón y muestras nuevamente. Compara el número de coincidencias y los grupos capturados con el resultado de JavaScript. Una diferencia aquí es tu error de portabilidad, detectado antes de que llegue a producción.

Paso 4: Corrige la divergencia usando las tablas anteriores#

Cuando los resultados difieran, asigna el síntoma a la sección relevante: anclajes, lookbehind, sintaxis de grupos con nombre, cadena de reemplazo o banderas Unicode. Aplica la reescritura portátil (grupos numerados, [\s\S] en lugar de dotall, lookbehind de ancho fijo) y vuelve a ejecutar ambos hasta que coincidan.

Si parte del patrón está haciendo algo que no puedes descifrar, pegarlo en un explicador de código en lenguaje sencillo puede desglosar cada token y marcar las partes específicas del motor, lo que acelera la corrección.

Cómo escribir expresiones regulares portátiles: una lista de verificación breve#

Cuando un patrón debe funcionar tanto en JavaScript como en Python sin bifurcaciones, limítate al subconjunto común:

  • Usa grupos numerados y referencias inversas \1, no grupos con nombre.
  • Usa [\s\S] en lugar de depender del indicador dotall para cruzar saltos de línea.
  • Mantén las inspecciones hacia atrás de ancho fijo para que el re de Python las acepte.
  • Evita los cuantificadores posesivos y los grupos atómicos.
  • Evita \A/\Z y las secuencias de escape de propiedades \p{...}.
  • Prueba las sustituciones por separado, ya que $1 vs \1 es un fallo silencioso.

Para tareas de modelado de datos donde también necesitas inspeccionar la salida estructurada junto con tus coincidencias, combinar el probador con un formateador JSON mantiene los resultados legibles mientras depuras.

Conclusión#

La división entre las expresiones regulares de JavaScript y Python rara vez está en la sintaxis que ves; está en las cuatro diferencias silenciosas que cambian el comportamiento sin generar un error: la falta de los anclajes \A/\Z, el lookbehind de ancho fijo en Python, la diferencia entre $1 y \1 en el reemplazo, y el soporte de propiedades Unicode. Memoriza esas cuatro, usa el subconjunto portátil cuando un patrón cruce lenguajes, y prueba el patrón exacto en ambos motores antes de confiar en él. Ese hábito convierte las expresiones regulares de una apuesta de copiar y pegar en algo que puedes mover de forma segura entre tu front-end y tu back-end.

Preguntas Frecuentes#

¿Por qué mi expresión regular funciona en Python pero no en JavaScript? Las causas más comunes son características exclusivas de Python en tu patrón: anclas \A/\Z, lookbehind de longitud variable, cuantificadores posesivos, grupos atómicos o grupos nombrados (?P<name>...). El sabor ECMAScript de JavaScript no soporta estos, por lo que el patrón lanza un error de sintaxis o simplemente no coincide con nada. Reescribe usando el subconjunto portátil (grupos numerados, lookbehind de ancho fijo, ^/$ simples) y prueba en ambos motores.

¿JavaScript soporta lookbehind como Python? Sí, y en un aspecto JavaScript es más capaz. Desde ES2018, JavaScript soporta tanto lookbehind de ancho fijo como de longitud variable, mientras que el módulo re integrado de Python solo soporta lookbehind de ancho fijo. Un lookbehind de longitud variable como (?<=cat|elephant) funciona en JavaScript moderno pero genera un error en re de Python. Para lookbehind variable en Python, usa el módulo de terceros regex.

¿Cuál es la diferencia entre $1 y \1 en reemplazos de expresiones regulares? Ambos referencian el mismo grupo capturado, pero cada uno pertenece a un motor diferente. Las cadenas de reemplazo de JavaScript usan $1 (y $<name> para grupos nombrados), mientras que re.sub de Python usa \1 (y \g<name>). Mezclarlos no genera un error; inserta los caracteres literales en su lugar, corrompiendo silenciosamente la salida. Siempre prueba las sustituciones con una cadena de muestra real en el idioma de destino.

¿Son compatibles los sabores de expresiones regulares de JavaScript y Python? Comparten la mayor parte de la sintaxis, aproximadamente el 80% común, incluyendo clases de caracteres, cuantificadores, alternancia y referencias inversas numeradas. Difieren en anclas, ancho de lookbehind, sintaxis de grupos nombrados, cadenas de reemplazo, banderas en línea, cuantificadores posesivos y escapes de propiedades Unicode. Un patrón restringido al subconjunto compartido funcionará sin cambios en ambos; cualquier cosa más allá necesita ramificación por motor o una reescritura.

¿Cómo coincido caracteres Unicode por propiedad en cada idioma? En JavaScript, activa la bandera u (o v) y usa escapes de propiedad como \p{Letter} o \p{Script=Greek}. El módulo re integrado de Python no soporta \p{...} en absoluto, así que instala el módulo de terceros regex para obtener escapes de propiedad. Por defecto, re de Python 3 ya hace que \w y \d sean conscientes de Unicode, mientras que JavaScript necesita la bandera u para una coincidencia correcta de puntos de código.

¿Cuál es la forma más rápida de encontrar la diferencia entre dos sabores de expresiones regulares? Ejecuta el patrón exacto y el mismo conjunto de cadenas de muestra a través de un probador de expresiones regulares de múltiples sabores, cambiando el motor de JavaScript a Python y comparando los recuentos de coincidencias y grupos capturados. Cualquier diferencia en la salida es tu error de portabilidad, aislado antes de que se envíe. Esto es más rápido y confiable que razonar sobre tablas de compatibilidad por sí solas, porque muestra el comportamiento real en tu entrada real.

regexjavascriptpythoncomparison

More from Molixa

Try Molixa Tools

50+ free AI tools for content creation, SEO, coding, and more. No signup, no watermark.

Explore all tools