← Back to Blog
·8 min read

Python vs JavaScript Regex: The Same Syntax, Completely Different Behavior

Developers who use regex in both Python and JavaScript carry the dangerous assumption that the patterns are interchangeable. They look identical. The behavior is not. Here are the specific differences that will catch you off guard — and have already caught most full-stack developers at least once.

Raw strings: Python needs them, JavaScript does not

# Python — without raw string, backslashes need double-escaping:
import re
re.search("\d+", text)    # \d is a regex digit, but Python processes \ first
re.search(r"d+", text)    # r"..." is a raw string — no Python escape processing
                           # This is the correct, standard Python regex style

// JavaScript — no raw string concept, always need double backslash:
/d+/.test(text)           // regex literal — no extra escaping
new RegExp("\d+").test(text)  // string constructor — needs double backslash

In Python, always use raw strings (r"...") for regex patterns. Not doing so is a source of subtle bugs that often work by accident (since "\d" is not a recognized escape sequence in Python and is treated as the literal two characters\ and d, which in a regex matches the same as \d— but this is undefined behavior in strict mode).

Match anchoring: Python's re.match() vs re.fullmatch() vs re.search()

# Python — three different match functions with different anchoring:
import re

re.search(r"d+", "abc123def")   # Finds match anywhere: "123"
re.match(r"d+", "abc123def")    # Must match at START: None (starts with 'a')
re.match(r"d+", "123abc")       # Matches at start, doesn't require full match: "123"
re.fullmatch(r"d+", "123abc")   # Must match ENTIRE string: None
re.fullmatch(r"d+", "123")      # Full match: "123"

// JavaScript equivalent:
/d+/.test("abc123def")          // true — like re.search()
/^d+$/.test("123abc")           // false — anchored like re.fullmatch()
/^d+/.test("123abc")            // true — anchored at start like re.match()

The most common mistake: Python developers use re.match() thinking it validates the entire string, then are surprised when re.match(r"\d+", "123abc")returns a match. Use re.fullmatch() for validation, re.search() for finding patterns in text.

Named groups: different syntax

# Python: (?P<name>...) syntax for named groups
import re
m = re.search(r"(?P<year>d{4})-(?P<month>d{2})", "2026-05")
m.group('year')     # "2026"
m.groupdict()       # {'year': '2026', 'month': '05'}

// JavaScript (ES2018+): (?<name>...) syntax
const m = "2026-05".match(/(?<year>d{4})-(?<month>d{2})/);
m.groups.year       // "2026"

// Python 3.7+ also supports (?<name>...) in some contexts but
// (?P<name>...) is the standard and recommended Python syntax

Finding all matches: re.findall() vs str.matchAll()

# Python:
import re

# No groups — returns list of matched strings:
re.findall(r"d+", "a1 b23 c456")  # ["1", "23", "456"]

# One group — returns list of group matches:
re.findall(r"(d+)", "a1 b23 c456")  # ["1", "23", "456"] (same here)

# Multiple groups — returns list of tuples:
re.findall(r"([a-z])(d+)", "a1 b23 c456")  # [("a","1"), ("b","23"), ("c","456")]

// JavaScript (ES2020+) — matchAll returns an iterator of match objects:
[..."a1 b23 c456".matchAll(/([a-z])(d+)/g)]
  .map(m => ({ letter: m[1], num: m[2] }))
// [{letter:"a", num:"1"}, {letter:"b", num:"23"}, {letter:"c", num:"456"}]

Flags: mostly equivalent, partly different

# Python flags (passed as second arg to re functions):
re.IGNORECASE (re.I)  → /pattern/i in JS
re.MULTILINE  (re.M)  → /pattern/m in JS
re.DOTALL     (re.S)  → /pattern/s in JS
re.VERBOSE    (re.X)  → no JS equivalent (allows whitespace+comments in pattern)
re.UNICODE    (re.U)  → default in Python 3 (no flag needed)

# Python also allows combining flags:
re.search(r"d+", text, re.IGNORECASE | re.MULTILINE)

// JS uses flag string:
/d+/im

# Python's re.VERBOSE (re.X) is the biggest feature gap:
# It allows multi-line patterns with whitespace and # comments.
# JavaScript has no equivalent — regex literals cannot span lines.
pattern = re.compile(r"""
    (d{4})  # year
    -
    (d{2})  # month
    -
    (d{2})  # day
""", re.VERBOSE)

Python's re.VERBOSEmode is legitimately better for complex patterns — it allows comments directly in the regex. If you write complex patterns in Python, use it. JavaScript's lack of an equivalent is a real shortcoming; the workaround is a comment above the regex.

The practical takeaway

Regex patterns are mostly portable between Python and JavaScript for simple cases. The differences surface in: anchoring behavior (Python's re.match() is not equivalent to a JavaScript anchored pattern), named group syntax ((?P<name>) vs (?<name>)), and how all-matches are returned. The stateful lastIndexbehavior of JavaScript'sg flag has no equivalent in Python. When porting regex-heavy code between languages, test every pattern — do not assume portability.

Published June 4, 2026 · By the utili.dev Team