Le plus grand piège entre les regex JavaScript et Python est que les deux moteurs partagent la plupart de la même syntaxe, donc un motif semble portable jusqu'à ce qu'il se comporte silencieusement mal. JavaScript utilise le dialecte ECMAScript ; Python utilise son propre module re, proche de PCRE. Ils s'accordent sur les 80% communs, puis divergent sur les ancres, les lookbehind, les chaînes de remplacement et Unicode. Ce guide répertorie chaque différence qui casse réellement le code, afin que vous puissiez écrire des motifs qui survivent au copier-coller entre les langages.
Si vous avez déjà pris une regex sur Stack Overflow et l'avez vue fonctionner dans un environnement mais échouer ou ne rien retourner dans un autre, c'est pourquoi : la réponse a été écrite pour un moteur. Vous trouverez ci-dessous la carte complète, avec des exemples et un moyen rapide de tester le même motif dans les deux dialectes avant de le déployer.
JavaScript vs Python Regex en un coup d'œil#
Voici la version courte avant l'analyse approfondie. Ce sont les différences qui causent de véritables bugs, pas des détails insignifiants.
| Fonctionnalité | JavaScript (ECMAScript) | Python (re) |
|---|---|---|
| Ancres début/fin de chaîne | Aucune (^/$ seulement) | \A et \Z |
| Lookbehind | Fixe et variable (ES2018+) | Largeur fixe uniquement |
Quantificateurs possessifs (a++) | Non supporté | Supporté (Python 3.11+) |
Groupes atomiques (?>...) | Non supporté | Supporté (Python 3.11+) |
| Syntaxe groupe nommé | (?<name>...) | (?P<name>...) |
| Référence arrière nommée | \k<name> | (?P=name) |
| Référence groupe dans remplacement | $1, $<name> | \1, \g<name> |
Drapeau . correspond aux sauts de ligne | s (dotall) | re.DOTALL / re.S |
Échappements propriétés Unicode \p{...} | Nécessite drapeau u ou v | Pas intégré (utiliser module regex) |
Drapeaux en ligne (?i) | Non supporté | Supporté |
| Gestion Unicode par défaut | Unités de code UTF-16 | Unicode par défaut dans re |
Le reste de cet article explique chaque ligne, pourquoi elle est importante et comment réécrire un motif pour qu'il fonctionne dans les deux.
Astuce rapide : lorsqu'un motif fonctionne dans un langage mais pas dans l'autre, la cause est presque toujours les ancres, le lookbehind ou la syntaxe de remplacement. Vérifiez ces trois points d'abord avant de supposer que l'expression régulière elle-même est erronée.
Ancres : l'écart entre \A et \Z#
C'est la source la plus fréquente de bugs du type "fonctionne en Python, plante en JavaScript". Python prend en charge deux jeux d'ancres, JavaScript un seul.
En Python, ^ et $ correspondent au début et à la fin de chaque ligne lorsque le drapeau re.MULTILINE est activé, tandis que \A et \Z correspondent toujours uniquement au début et à la fin absolus de la chaîne entière, indépendamment des drapeaux. Cette distinction permet d'écrire un validateur strict de chaîne entière qui ignore le comportement multiligne.
JavaScript n'a ni \A ni \Z. Il ne propose que ^ et $, dont la signification change selon le drapeau m (multiligne). Ainsi, un motif Python comme \A\d{4}\Z n'a pas d'équivalent direct en JavaScript.
// Python : re.match(r"\A\d{4}\Z", text)
// Équivalent JavaScript (sans le drapeau multiligne, donc ^ et $ portent sur la chaîne entière) :
/^\d{4}$/.test(text);
La solution est d'omettre le drapeau m en JavaScript et d'utiliser simplement ^/$, qui ancrent alors la chaîne entière. Attention : le $ de JavaScript peut aussi correspondre juste avant un saut de ligne final, donc pour une stricte fin de chaîne, utilisez $(?![\s\S]). Oublier cela permet à une entrée multiligne de contourner un validateur qui semblait infaillible.
Lookbehind : largeur fixe vs variable#
Le lookbehind est l'endroit où les deux moteurs échangent leurs rôles, ce qui surprend ceux qui pensent que Python est toujours le moteur le plus performant.
- JavaScript a ajouté le lookbehind en ES2018, et il prend en charge les lookbehinds de largeur fixe et variable.
(?<=\$\d+)et(?<=chat|éléphant)fonctionnent tous les deux. - Le module
reintégré de Python ne prend en charge que le lookbehind de largeur fixe.(?<=chat|chien)fonctionne car les deux alternatives font trois lettres, mais(?<=chat|éléphant)lèvere.error: look-behind requires fixed-width pattern.
Ainsi, un lookbehind de largeur variable qui fonctionne parfaitement dans un navigateur moderne échouera à la compilation en Python. Si vous avez besoin d'un lookbehind variable en Python, installez le module tiers regex qui le prend en charge, ou restructurez le motif pour utiliser un groupe de capture à la place d'un lookbehind.
# Échoue dans re de Python :
re.compile(r"(?<=USD |EUR )\d+") # longueurs différentes -> erreur
# Solution : capturer et référencer, ou compléter les alternatives à largeur égale
re.compile(r"(?:USD|EUR) (\d+)") # capture le nombre dans le groupe 1
Si vous n'êtes pas encore à l'aise avec le comportement réel du lookbehind et du lookahead, il vaut la peine de lire les mécanismes plus profonds dans notre guide regex lookahead et lookbehind avant de porter des motifs entre les langages.
Syntaxe des groupes de capture et des références arrière#
Les deux moteurs prennent en charge les groupes nommés, mais la syntaxe diffère, et il s'agit d'une erreur de compilation stricte plutôt que d'une incompatibilité silencieuse.
JavaScript utilise (?<name>...). Python utilise (?P<name>...) avec ce P supplémentaire. Si vous les mélangez, le motif ne compilera tout simplement pas.
| Action | JavaScript | Python |
|---|---|---|
| Définir un groupe nommé | (?<year>\d{4}) | (?P<year>\d{4}) |
| Référence arrière dans le motif | \k<year> | (?P=year) ou \1 |
| Référence arrière numérotée | \1 | \1 |
Les références arrière numérotées (\1, \2) sont identiques dans les deux langages, ce qui en fait le choix le plus sûr lorsque vous souhaitez qu'un même motif fonctionne sans modification dans les deux langages. Dès que vous utilisez des groupes nommés, vous devez adapter votre motif par moteur.
Indicateurs en ligne#
Python permet de définir des indicateurs dans le motif avec (?i) pour l'insensibilité à la casse, (?m) pour le mode multiligne, etc., y compris la forme limitée (?i:...). JavaScript ne prenait historiquement pas en charge les indicateurs en ligne ; les indicateurs étaient uniquement placés sur le littéral (/pattern/i) ou le constructeur RegExp. Les indicateurs limités en ligne (?i:...) sont un ajout récent d'ECMAScript et ne sont pas encore universels dans tous les environnements d'exécution, donc ne comptez pas dessus pour du code portable.
Syntaxe de chaîne de remplacement (le bug silencieux)#
Celui-ci est dangereux car il ne génère pas d'erreur. Il produit simplement un résultat erroné, bien plus difficile à détecter lors d'une relecture qu'un plantage.
Lorsque vous référencez un groupe capturé dans une chaîne de remplacement, JavaScript utilise le signe dollar et Python utilise une barre oblique inverse.
// JavaScript : $1 référence le groupe 1, $<name> référence un groupe nommé
"2026-06-25".replace(/(\d{4})-(\d{2})/, "$2/$1"); // "06/2026-25"
# Python : \1 référence le groupe 1, \g<name> référence un groupe nommé
re.sub(r"(\d{4})-(\d{2})", r"\2/\1", "2026-06-25") # "06/2026-25"
Si vous collez une chaîne de remplacement JavaScript avec $1 dans re.sub de Python, Python traite $1 comme les caractères littéraux dollar-un, et votre remplacement insère silencieusement $1 dans la sortie au lieu de la valeur capturée. Aucune erreur, juste du texte corrompu. L'inverse se produit aussi : un \1 dans un remplacement JavaScript est interprété comme une échappement et provoque une erreur.
Attention : les erreurs de syntaxe de chaîne de remplacement ne lèvent pas d'exceptions. Testez toujours une substitution sur un échantillon réel dans la langue cible, pas seulement le motif de correspondance.
Le drapeau Dotall et la correspondance multiligne#
Par défaut, . ne correspond pas à un saut de ligne dans les deux moteurs. Pour changer cela, les noms des drapeaux diffèrent.
- JavaScript : le drapeau
s(introduit en ES2018), comme dans/foo.bar/s. - Python :
re.DOTALLou sa forme courtere.S, passé àre.compileou à l'appel de fonction.
Le comportement une fois activé est identique, mais JavaScript attache les drapeaux sous forme de lettres après la barre oblique fermante ou comme second argument de RegExp, tandis que Python passe des constantes de drapeaux combinées avec l'opérateur OU binaire, comme re.DOTALL | re.IGNORECASE.
Une habitude portable : évitez de compter sur . pour traverser les lignes. Utilisez la classe explicite [\s\S] dans les deux langages, qui correspond à tout caractère, y compris les sauts de ligne, sans aucun drapeau. Cette astuce supprime l'un des décalages de drapeaux les plus courants entre les langages.
Unicode : échappements de propriétés et comportement par défaut#
Unicode est le domaine où les moteurs divergent le plus subtilement, et cela impacte le plus durement les entrées non ASCII.
JavaScript traite les chaînes comme des unités de code UTF-16 par défaut. Sans le drapeau u (Unicode), un caractère astral comme un emoji est composé de deux unités de code, et . ou un quantificateur peut en correspondre à la moitié, produisant du mojibake. L'ajout du drapeau u fait fonctionner l'expression régulière sur les points de code et déverrouille les échappements de propriétés Unicode comme \p{Letter} et \p{Script=Greek}. Le nouveau drapeau v étend cela avec des opérations sur les ensembles.
Les chaînes Python 3 sont Unicode par défaut, donc \w et \d correspondent déjà aux lettres et chiffres Unicode, sauf si vous passez re.ASCII. Cependant, le module re intégré de Python ne prend pas en charge les échappements de propriétés \p{...}. Si vous avez besoin d'échappements de propriétés en Python, vous installez le module tiers regex, qui les ajoute.
| Besoin | JavaScript | Python re |
|---|---|---|
Correspondance par propriété Unicode \p{L} | Drapeau u ou v | Non supporté (utiliser le module regex) |
| Correspondance correcte par point de code | Nécessite le drapeau u | Par défaut |
| Caractères de mot ASCII uniquement | Par défaut sans u est partiel | Drapeau re.ASCII |
En pratique : en JavaScript, utilisez le drapeau u dès que votre entrée peut contenir du texte non ASCII. En Python, attendez-vous à ce que \w/\d soient compatibles Unicode par défaut, mais n'utilisez pas \p{...} sauf si la bibliothèque regex est installée.
Quantificateurs possessifs et groupes atomiques#
Ce sont des fonctionnalités de contrôle du retour en arrière. Les quantificateurs possessifs (a++, a*+, a?+) et les groupes atomiques ((?>...)) empêchent le moteur de revenir en arrière dans une section déjà mise en correspondance, ce qui peut éviter un retour en arrière catastrophique sur une entrée malveillante. Le module re intégré de Python a ajouté les deux à partir de Python 3.11. Les expressions régulières ECMAScript de JavaScript ne prennent en charge ni l'un ni l'autre.
Ainsi, un motif utilisant (?>...) ou a++ issu d'un code Python 3.11+ génère une erreur de syntaxe en JavaScript. Pour obtenir un contrôle similaire en JavaScript, vous réécrivez le motif avec une astuce d'anticipation et de référence arrière (plus difficile à lire) ou vous restructurez l'expression régulière pour qu'elle soit sans ambiguïté dès le départ.
Comment tester le même motif dans les deux moteurs#
La méthode fiable pour éviter tous ces pièges est de tester le motif exact sur un échantillon de texte réel dans chaque moteur avant de l'utiliser définitivement. Lire un tableau de compatibilité vous dit ce qui devrait se passer ; exécuter le motif vous dit ce qui se passe réellement.
Étape 1 : Écrire le motif une fois et définir vos chaînes de test#
Commencez par le motif et trois à cinq exemples d'entrée qui couvrent vos cas limites : une correspondance parfaite, un quasi-échec, un cas multiligne et un cas non ASCII si pertinent. Gardez-les sous les yeux pour pouvoir confirmer les correspondances visuellement.
Étape 2 : L'exécuter dans le moteur JavaScript#
Collez le motif et les exemples dans un testeur de regex multi-moteur gratuit configuré sur le moteur JavaScript. Confirmez les correspondances, les groupes de capture et tout résultat de remplacement. Surveillez particulièrement le comportement des ancres et la gestion Unicode ici.
Étape 3 : Passer au moteur Python et ré-exécuter#
Changez le moteur pour Python et exécutez à nouveau le motif et les exemples identiques. Comparez le nombre de correspondances et les groupes capturés avec le résultat JavaScript. Une différence ici est votre bogue de portabilité, détecté avant qu'il n'atteigne la production.
Étape 4 : Corriger la divergence à l'aide des tableaux ci-dessus#
Lorsque les résultats diffèrent, associez le symptôme à la section pertinente : ancres, lookbehind, syntaxe de groupe nommé, chaîne de remplacement ou indicateurs Unicode. Appliquez la réécriture portable (groupes numérotés, [\s\S] à la place de dotall, lookbehind de largeur égale) et ré-exécutez les deux jusqu'à ce qu'ils concordent.
Si une partie du motif fait quelque chose que vous ne pouvez pas décoder, la coller dans un explicateur de code en langage clair peut décomposer chaque jeton et signaler les éléments spécifiques au moteur, ce qui accélère la correction.
Écrire des expressions régulières portables : une checklist concise#
Lorsqu'un motif doit fonctionner à la fois en JavaScript et en Python sans branchement, respectez le sous-ensemble commun :
- Utilisez des groupes numérotés et les références arrière
\1, pas de groupes nommés. - Utilisez
[\s\S]au lieu de compter sur le drapeau dotall pour traverser les sauts de ligne. - Gardez les lookbehinds de largeur fixe pour que le
rede Python les accepte. - Évitez les quantificateurs possessifs et les groupes atomiques.
- Évitez
\A/\Zet les échappements de propriétés\p{...}. - Testez les substitutions séparément, car
$1vs\1est un échec silencieux.
Pour le travail de mise en forme des données où vous devez également inspecter la sortie structurée en parallèle de vos correspondances, associer le testeur à un formateur JSON permet de garder les résultats de correspondance lisibles pendant le débogage.
Conclusion#
La différence entre les regex JavaScript et Python ne réside pas dans la syntaxe visible, mais dans quatre différences silencieuses qui modifient le comportement sans générer d'erreur : l'absence des ancres \A/\Z, les lookbehind de largeur fixe uniquement en Python, la différence entre $1 et \1 pour les remplacements, et la prise en charge des échappements de propriétés Unicode. Mémorisez ces quatre points, utilisez par défaut le sous-ensemble portable lorsqu'un motif traverse les langages, et testez le motif exact dans les deux moteurs avant de lui faire confiance. Cette habitude transforme les regex d'un pari copier-coller en quelque chose que vous pouvez déplacer en toute sécurité entre votre front-end et votre back-end.
Foire aux questions#
Pourquoi mon regex fonctionne-t-il en Python mais pas en JavaScript ?
Les causes les plus courantes sont les fonctionnalités propres à Python dans votre motif : les ancres \A/\Z, les lookbehind de longueur variable, les quantificateurs possessifs, les groupes atomiques ou les groupes nommés (?P<name>...). La saveur ECMAScript de JavaScript ne les supporte pas, donc le motif génère une erreur de syntaxe ou ne correspond silencieusement à rien. Réécrivez en utilisant le sous-ensemble portable (groupes numérotés, lookbehind de largeur fixe, ^/$ simples) et testez dans les deux moteurs.
JavaScript supporte-t-il le lookbehind comme Python ?
Oui, et à un égard JavaScript est plus performant. Depuis ES2018, JavaScript supporte les lookbehind de largeur fixe et variable, tandis que le module re intégré de Python ne supporte que les lookbehind de largeur fixe. Un lookbehind de longueur variable comme (?<=cat|elephant) fonctionne en JavaScript moderne mais génère une erreur dans re de Python. Pour un lookbehind variable en Python, utilisez le module tiers regex.
Quelle est la différence entre $1 et \1 dans les remplacements regex ?
Ils référencent le même groupe capturé, mais chacun appartient à un moteur différent. Les chaînes de remplacement JavaScript utilisent $1 (et $<name> pour les groupes nommés), tandis que re.sub de Python utilise \1 (et \g<name>). Les mélanger ne génère pas d'erreur ; il insère les caractères littéraux à la place, corrompant silencieusement la sortie. Testez toujours les substitutions sur un échantillon réel dans la langue cible.
Les saveurs regex de JavaScript et Python sont-elles compatibles ? Elles partagent la plupart de la syntaxe, environ 80% du commun, y compris les classes de caractères, les quantificateurs, l'alternance et les références arrière numérotées. Elles divergent sur les ancres, la largeur du lookbehind, la syntaxe des groupes nommés, les chaînes de remplacement, les drapeaux en ligne, les quantificateurs possessifs et les échappements de propriétés Unicode. Un motif limité au sous-ensemble partagé fonctionnera inchangé dans les deux ; tout ce qui dépasse nécessite une ramification par moteur ou une réécriture.
Comment faire correspondre des caractères Unicode par propriété dans chaque langage ?
En JavaScript, activez le drapeau u (ou v) et utilisez des échappements de propriété comme \p{Letter} ou \p{Script=Greek}. Le module re intégré de Python ne supporte pas du tout \p{...}, donc installez le module tiers regex pour obtenir les échappements de propriété. Par défaut, re de Python 3 rend déjà \w et \d sensibles à Unicode, tandis que JavaScript a besoin du drapeau u pour une correspondance correcte des points de code.
Quel est le moyen le plus rapide de trouver la différence entre deux saveurs regex ? Exécutez le motif exact et le même ensemble de chaînes d'échantillon via un testeur regex multi-saveur, en passant le moteur de JavaScript à Python et en comparant les nombres de correspondances et les groupes capturés. Toute différence dans la sortie est votre bogue de portabilité, isolé avant d'être livré. C'est plus rapide et plus fiable que de raisonner uniquement sur les tableaux de compatibilité, car cela montre le comportement réel sur votre entrée réelle.



