PythonMastery
reference 5 min read · lesson 10 of 25 in Reference

Python Regex Cheat Sheet

1 · The lesson

read

A regular expression (regex) is a pattern that describes text, such as "four digits" or "a word followed by @". Python's re module uses a pattern to find, split or replace text. For the full tutorial, see Regex.

Always write patterns as raw strings, with an r before the quote: r"\d+". In a raw string Python leaves the backslashes alone, so they reach re as you wrote them. The examples use this string:

python
import re

s = "Order 66 shipped 2026-03-14 to ada@dev.io"

1. The functions

python
print(re.findall(r"\d+", s))                # ['66', '2026', '03', '14']
print(re.search(r"\d+", s).group())         # 66
print(re.match(r"\d+", s))                  # None
print(bool(re.fullmatch(r"\d{4}", "2026"))) # True
print(re.sub(r"\d", "#", "a1b22"))          # a#b##
print(re.split(r"[,;]\s*", "a, b;c"))       # ['a', 'b', 'c']
print([m.span() for m in re.finditer(r"\d+", "a1 b22")])  # [(1, 2), (4, 6)]
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

s = _AutoMock('s')
re = _AutoMock('re')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier
FunctionWhat it doesReturns
re.findall(pat, s)Finds every matchA list of strings (tuples if the pattern has groups)
re.search(pat, s)Finds the first match anywhereA match object, or None
re.match(pat, s)Looks only at the start of the stringA match object, or None
re.fullmatch(pat, s)The whole string must matchA match object, or None
re.finditer(pat, s)Finds every match, one at a timeMatch objects, which also give the position
re.sub(pat, repl, s)Replaces every matchA new string
re.split(pat, s)Splits the string wherever the pattern matchesA list of strings
re.compile(pat)Prepares a pattern once, so you can use it many timesA pattern object
python
DATE = re.compile(r"\d{4}-\d\d-\d\d")
print(DATE.search(s).group())   # 2026-03-14
+ setup added so this can run · re-runs 2 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

re = _AutoMock('re')
s = _AutoMock('s')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

2. Character classes

A character class matches one character from a set.

PatternMatches
\done digit
\wone word character: a letter, a digit or _
\sone whitespace character: a space, a tab or a new line
\D, \W, \Sone character that is NOT a digit, NOT a word character, or NOT whitespace
[aeiou]one of the characters inside the brackets
[a-z]one character in the range a to z
[^a-z]one character that is NOT in the brackets (^ first means "not")
.any one character, except a new line
python
print(re.findall(r"\w+", "it's ok_2"))       # ['it', 's', 'ok_2']
print(re.findall(r"[aeiou]", "regex"))       # ['e', 'e']
print(re.findall(r"[^a-z ]", "ab C1"))       # ['C', '1']
print(re.findall(r"a.c", "abc a-c ac"))      # ['abc', 'a-c']
print(re.findall(r"\.", "v1.2.3"))           # ['.', '.']
+ setup added so this can run · re-runs 3 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

re = _AutoMock('re')
s = _AutoMock('s')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

To match a real dot, escape it with a backslash: \..

3. How many

A quantifier after a character or group says how many times it may repeat.

PatternMeans
+one or more
*zero or more
?zero or one: the item is optional
{3}exactly three
{2,3}two to three
{2,}two or more
python
print(re.findall(r"lo+l", "ll lol loool"))        # ['lol', 'loool']
print(re.findall(r"lo*l", "ll lol"))              # ['ll', 'lol']
print(re.findall(r"colou?r", "color colour"))     # ['color', 'colour']
print(re.findall(r"\d{3}", "12345678"))           # ['123', '456']
print(re.findall(r"\d{2,3}", "12345678"))         # ['123', '456', '78']
+ setup added so this can run · re-runs 4 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

re = _AutoMock('re')
s = _AutoMock('s')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

Quantifiers are greedy: they take as much text as they can. Add ? after them to make them lazy, so they take as little as they can:

python
print(re.findall(r"<.+>", "<a><b>"))    # ['<a><b>']
print(re.findall(r"<.+?>", "<a><b>"))   # ['<a>', '<b>']
+ setup added so this can run · re-runs 5 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

re = _AutoMock('re')
s = _AutoMock('s')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

4. Anchors

An anchor matches a position, not a character.

python
print(bool(re.search(r"^\d+$", "2026")))               # True
print(re.findall(r"\bcat\b", "cat concat cat."))        # ['cat', 'cat']
print(re.findall(r"^\w+", "a b\nc d", re.M))            # ['a', 'c']
+ setup added so this can run · re-runs 6 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

re = _AutoMock('re')
s = _AutoMock('s')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
    'print(re.findall(r"<.+>", "<a><b>"))    # [\'<a><b>\']\nprint(re.findall(r"<.+?>", "<a><b>"))   # [\'<a>\', \'<b>\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

^ is the start of the string and $ is the end, so ^\d+$ checks the whole string. \b is a word boundary, the edge of a word, so \bcat\b finds only the whole word "cat". With the re.M flag, ^ and $ work on each line.

5. Groups

Parentheses ( ) capture part of the match, so you can read it on its own. Groups are numbered from 1, and group 0 is the whole match.

python
m = re.search(r"(\w+)@(\S+)", s)
print(m.group(1), m.group(2))   # ada dev.io
print(m.group(0))               # ada@dev.io

print(re.findall(r"(\d+)-(\d+)", "1-2 3-4"))      # [('1', '2'), ('3', '4')]
print(re.findall(r"(?:ab)+", "abab x ab"))        # ['abab', 'ab']
print(re.findall(r"cat|dog", "cat, dog, cow"))    # ['cat', 'dog']
print(re.findall(r"(\w)\1", "book keeper"))       # ['o', 'e']
+ setup added so this can run · re-runs 7 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

s = _AutoMock('s')
re = _AutoMock('re')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
    'print(re.findall(r"<.+>", "<a><b>"))    # [\'<a><b>\']\nprint(re.findall(r"<.+?>", "<a><b>"))   # [\'<a>\', \'<b>\']',
    'print(bool(re.search(r"^\\d+$", "2026")))               # True\nprint(re.findall(r"\\bcat\\b", "cat concat cat."))        # [\'cat\', \'cat\']\nprint(re.findall(r"^\\w+", "a b\\nc d", re.M))            # [\'a\', \'c\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

When a pattern has groups, findall returns only the text of the groups: a string for each match with one group, or a tuple for each match with several groups. (?: ) groups without capturing. | matches either side. \1 matches the same text that group 1 matched, so (\w)\1 finds a doubled letter.

6. Named groups

(?P<name>...) gives a group a name, which is easier to read than a number.

python
m = re.search(r"(?P<y>\d+)-(?P<m>\d+)", s)
print(m["y"])            # 2026
print(m.group("y"))      # 2026
print(m.groupdict())     # {'y': '2026', 'm': '03'}
+ setup added so this can run · re-runs 8 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

s = _AutoMock('s')
re = _AutoMock('re')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
    'print(re.findall(r"<.+>", "<a><b>"))    # [\'<a><b>\']\nprint(re.findall(r"<.+?>", "<a><b>"))   # [\'<a>\', \'<b>\']',
    'print(bool(re.search(r"^\\d+$", "2026")))               # True\nprint(re.findall(r"\\bcat\\b", "cat concat cat."))        # [\'cat\', \'cat\']\nprint(re.findall(r"^\\w+", "a b\\nc d", re.M))            # [\'a\', \'c\']',
    'm = re.search(r"(\\w+)@(\\S+)", s)\nprint(m.group(1), m.group(2))   # ada dev.io\nprint(m.group(0))               # ada@dev.io\n\nprint(re.findall(r"(\\d+)-(\\d+)", "1-2 3-4"))      # [(\'1\', \'2\'), (\'3\', \'4\')]\nprint(re.findall(r"(?:ab)+", "abab x ab"))        # [\'abab\', \'ab\']\nprint(re.findall(r"cat|dog", "cat, dog, cow"))    # [\'cat\', \'dog\']\nprint(re.findall(r"(\\w)\\1", "book keeper"))       # [\'o\', \'e\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

7. Look around

A lookaround checks the text next to a match without making it part of the match.

python
print(re.findall(r"\d+(?=px)", "10px 2em 5px"))   # ['10', '5']
print(re.findall(r"(?<=\$)\d+", "$5 and 7"))      # ['5']
print(re.findall(r"\b\d+\b(?!%)", "5% 7"))        # ['7']
+ setup added so this can run · re-runs 9 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

re = _AutoMock('re')
s = _AutoMock('s')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
    'print(re.findall(r"<.+>", "<a><b>"))    # [\'<a><b>\']\nprint(re.findall(r"<.+?>", "<a><b>"))   # [\'<a>\', \'<b>\']',
    'print(bool(re.search(r"^\\d+$", "2026")))               # True\nprint(re.findall(r"\\bcat\\b", "cat concat cat."))        # [\'cat\', \'cat\']\nprint(re.findall(r"^\\w+", "a b\\nc d", re.M))            # [\'a\', \'c\']',
    'm = re.search(r"(\\w+)@(\\S+)", s)\nprint(m.group(1), m.group(2))   # ada dev.io\nprint(m.group(0))               # ada@dev.io\n\nprint(re.findall(r"(\\d+)-(\\d+)", "1-2 3-4"))      # [(\'1\', \'2\'), (\'3\', \'4\')]\nprint(re.findall(r"(?:ab)+", "abab x ab"))        # [\'abab\', \'ab\']\nprint(re.findall(r"cat|dog", "cat, dog, cow"))    # [\'cat\', \'dog\']\nprint(re.findall(r"(\\w)\\1", "book keeper"))       # [\'o\', \'e\']',
    'm = re.search(r"(?P<y>\\d+)-(?P<m>\\d+)", s)\nprint(m["y"])            # 2026\nprint(m.group("y"))      # 2026\nprint(m.groupdict())     # {\'y\': \'2026\', \'m\': \'03\'}',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier
PatternMeans
(?=px)must be followed by px
(?!%)must not be followed by %
(?<=\$)must come after $
(?<!\$)must not come after $

8. Replace

python
print(re.sub(r"\s+", " ", "a  b\n c"))                  # a b c
print(re.sub(r"(\w+)-(\w+)", r"\2-\1", "a-b"))          # b-a
print(re.sub(r"(?P<d>\d)", r"[\g<d>]", "a1"))           # a[1]
print(re.sub(r"\d", lambda m: m[0] * 2, "a1"))          # a11
print(re.sub(r"o", "0", "foo", count=1))                # f0o
+ setup added so this can run · re-runs 10 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

re = _AutoMock('re')
s = _AutoMock('s')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
    'print(re.findall(r"<.+>", "<a><b>"))    # [\'<a><b>\']\nprint(re.findall(r"<.+?>", "<a><b>"))   # [\'<a>\', \'<b>\']',
    'print(bool(re.search(r"^\\d+$", "2026")))               # True\nprint(re.findall(r"\\bcat\\b", "cat concat cat."))        # [\'cat\', \'cat\']\nprint(re.findall(r"^\\w+", "a b\\nc d", re.M))            # [\'a\', \'c\']',
    'm = re.search(r"(\\w+)@(\\S+)", s)\nprint(m.group(1), m.group(2))   # ada dev.io\nprint(m.group(0))               # ada@dev.io\n\nprint(re.findall(r"(\\d+)-(\\d+)", "1-2 3-4"))      # [(\'1\', \'2\'), (\'3\', \'4\')]\nprint(re.findall(r"(?:ab)+", "abab x ab"))        # [\'abab\', \'ab\']\nprint(re.findall(r"cat|dog", "cat, dog, cow"))    # [\'cat\', \'dog\']\nprint(re.findall(r"(\\w)\\1", "book keeper"))       # [\'o\', \'e\']',
    'm = re.search(r"(?P<y>\\d+)-(?P<m>\\d+)", s)\nprint(m["y"])            # 2026\nprint(m.group("y"))      # 2026\nprint(m.groupdict())     # {\'y\': \'2026\', \'m\': \'03\'}',
    'print(re.findall(r"\\d+(?=px)", "10px 2em 5px"))   # [\'10\', \'5\']\nprint(re.findall(r"(?<=\\$)\\d+", "$5 and 7"))      # [\'5\']\nprint(re.findall(r"\\b\\d+\\b(?!%)", "5% 7"))        # [\'7\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

In the replacement, \1 and \2 put the groups back, and \g<name> puts back a named group. If you pass a function instead of a replacement string, re.sub calls the function with each match, and the function returns the replacement text. count=1 replaces only the first match.

9. Flags

A flag changes how the whole pattern works. Combine flags with |, for example re.I | re.M.

FlagEffect
re.IIgnore upper and lower case
re.M^ and $ match at the start and end of each line
re.S. also matches a new line
re.XSpaces and # comments in the pattern are ignored, so you can spread a long pattern over several lines
re.A\w, \d and \s match only ASCII characters
python
print(re.findall(r"py", "Py PY", re.I))    # ['Py', 'PY']
print(re.findall(r"(?i)py", "Py"))         # ['Py']
print(re.findall(r"a.b", "a\nb", re.S))    # ['a\nb']

PHONE = re.compile(r"""
    (?P<area>\d{3}) -   # area code
    (?P<num>\d{4})      # local number
""", re.X)
print(PHONE.search("dial 415-9000").groupdict())  # {'area': '415', 'num': '9000'}
+ setup added so this can run · re-runs 11 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

re = _AutoMock('re')
s = _AutoMock('s')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
    'print(re.findall(r"<.+>", "<a><b>"))    # [\'<a><b>\']\nprint(re.findall(r"<.+?>", "<a><b>"))   # [\'<a>\', \'<b>\']',
    'print(bool(re.search(r"^\\d+$", "2026")))               # True\nprint(re.findall(r"\\bcat\\b", "cat concat cat."))        # [\'cat\', \'cat\']\nprint(re.findall(r"^\\w+", "a b\\nc d", re.M))            # [\'a\', \'c\']',
    'm = re.search(r"(\\w+)@(\\S+)", s)\nprint(m.group(1), m.group(2))   # ada dev.io\nprint(m.group(0))               # ada@dev.io\n\nprint(re.findall(r"(\\d+)-(\\d+)", "1-2 3-4"))      # [(\'1\', \'2\'), (\'3\', \'4\')]\nprint(re.findall(r"(?:ab)+", "abab x ab"))        # [\'abab\', \'ab\']\nprint(re.findall(r"cat|dog", "cat, dog, cow"))    # [\'cat\', \'dog\']\nprint(re.findall(r"(\\w)\\1", "book keeper"))       # [\'o\', \'e\']',
    'm = re.search(r"(?P<y>\\d+)-(?P<m>\\d+)", s)\nprint(m["y"])            # 2026\nprint(m.group("y"))      # 2026\nprint(m.groupdict())     # {\'y\': \'2026\', \'m\': \'03\'}',
    'print(re.findall(r"\\d+(?=px)", "10px 2em 5px"))   # [\'10\', \'5\']\nprint(re.findall(r"(?<=\\$)\\d+", "$5 and 7"))      # [\'5\']\nprint(re.findall(r"\\b\\d+\\b(?!%)", "5% 7"))        # [\'7\']',
    'print(re.sub(r"\\s+", " ", "a  b\\n c"))                  # a b c\nprint(re.sub(r"(\\w+)-(\\w+)", r"\\2-\\1", "a-b"))          # b-a\nprint(re.sub(r"(?P<d>\\d)", r"[\\g<d>]", "a1"))           # a[1]\nprint(re.sub(r"\\d", lambda m: m[0] * 2, "a1"))          # a11\nprint(re.sub(r"o", "0", "foo", count=1))                # f0o',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

(?i) at the start of a pattern sets the flag inside the pattern itself.

10. Useful patterns

These are loose patterns: good for finding things in text, not for strictly checking that input is valid.

python
print(re.findall(r"[\w.+-]+@[\w-]+\.[\w.]+", s))        # ['ada@dev.io']
print(re.findall(r"-?\d+(?:\.\d+)?", "-3.5 2"))          # ['-3.5', '2']
print(re.findall(r"\d{4}-\d{2}-\d{2}", s))               # ['2026-03-14']
print(re.findall(r"https?://\S+", "x http://a"))         # ['http://a']
print(re.findall(r"#[0-9A-Fa-f]{6}", "red #FF0000"))     # ['#FF0000']
print(re.sub(r"<[^>]+>", "", "<b>hi</b>"))               # hi
+ setup added so this can run · re-runs 12 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

s = _AutoMock('s')
re = _AutoMock('re')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
    'print(re.findall(r"<.+>", "<a><b>"))    # [\'<a><b>\']\nprint(re.findall(r"<.+?>", "<a><b>"))   # [\'<a>\', \'<b>\']',
    'print(bool(re.search(r"^\\d+$", "2026")))               # True\nprint(re.findall(r"\\bcat\\b", "cat concat cat."))        # [\'cat\', \'cat\']\nprint(re.findall(r"^\\w+", "a b\\nc d", re.M))            # [\'a\', \'c\']',
    'm = re.search(r"(\\w+)@(\\S+)", s)\nprint(m.group(1), m.group(2))   # ada dev.io\nprint(m.group(0))               # ada@dev.io\n\nprint(re.findall(r"(\\d+)-(\\d+)", "1-2 3-4"))      # [(\'1\', \'2\'), (\'3\', \'4\')]\nprint(re.findall(r"(?:ab)+", "abab x ab"))        # [\'abab\', \'ab\']\nprint(re.findall(r"cat|dog", "cat, dog, cow"))    # [\'cat\', \'dog\']\nprint(re.findall(r"(\\w)\\1", "book keeper"))       # [\'o\', \'e\']',
    'm = re.search(r"(?P<y>\\d+)-(?P<m>\\d+)", s)\nprint(m["y"])            # 2026\nprint(m.group("y"))      # 2026\nprint(m.groupdict())     # {\'y\': \'2026\', \'m\': \'03\'}',
    'print(re.findall(r"\\d+(?=px)", "10px 2em 5px"))   # [\'10\', \'5\']\nprint(re.findall(r"(?<=\\$)\\d+", "$5 and 7"))      # [\'5\']\nprint(re.findall(r"\\b\\d+\\b(?!%)", "5% 7"))        # [\'7\']',
    'print(re.sub(r"\\s+", " ", "a  b\\n c"))                  # a b c\nprint(re.sub(r"(\\w+)-(\\w+)", r"\\2-\\1", "a-b"))          # b-a\nprint(re.sub(r"(?P<d>\\d)", r"[\\g<d>]", "a1"))           # a[1]\nprint(re.sub(r"\\d", lambda m: m[0] * 2, "a1"))          # a11\nprint(re.sub(r"o", "0", "foo", count=1))                # f0o',
    'print(re.findall(r"py", "Py PY", re.I))    # [\'Py\', \'PY\']\nprint(re.findall(r"(?i)py", "Py"))         # [\'Py\']\nprint(re.findall(r"a.b", "a\\nb", re.S))    # [\'a\\nb\']\n\nPHONE = re.compile(r"""\n    (?P<area>\\d{3}) -   # area code\n    (?P<num>\\d{4})      # local number\n""", re.X)\nprint(PHONE.search("dial 415-9000").groupdict())  # {\'area\': \'415\', \'num\': \'9000\'}',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

Common mistakes

Calling .group() on no match. When nothing matches, re.search() returns None, and None.group() raises AttributeError. Check first:

python
if m := re.search(r"\d+", s):
    print(m.group())   # 66
+ setup added so this can run · re-runs 13 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

s = _AutoMock('s')
re = _AutoMock('re')
m = _AutoMock('m')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
    'print(re.findall(r"<.+>", "<a><b>"))    # [\'<a><b>\']\nprint(re.findall(r"<.+?>", "<a><b>"))   # [\'<a>\', \'<b>\']',
    'print(bool(re.search(r"^\\d+$", "2026")))               # True\nprint(re.findall(r"\\bcat\\b", "cat concat cat."))        # [\'cat\', \'cat\']\nprint(re.findall(r"^\\w+", "a b\\nc d", re.M))            # [\'a\', \'c\']',
    'm = re.search(r"(\\w+)@(\\S+)", s)\nprint(m.group(1), m.group(2))   # ada dev.io\nprint(m.group(0))               # ada@dev.io\n\nprint(re.findall(r"(\\d+)-(\\d+)", "1-2 3-4"))      # [(\'1\', \'2\'), (\'3\', \'4\')]\nprint(re.findall(r"(?:ab)+", "abab x ab"))        # [\'abab\', \'ab\']\nprint(re.findall(r"cat|dog", "cat, dog, cow"))    # [\'cat\', \'dog\']\nprint(re.findall(r"(\\w)\\1", "book keeper"))       # [\'o\', \'e\']',
    'm = re.search(r"(?P<y>\\d+)-(?P<m>\\d+)", s)\nprint(m["y"])            # 2026\nprint(m.group("y"))      # 2026\nprint(m.groupdict())     # {\'y\': \'2026\', \'m\': \'03\'}',
    'print(re.findall(r"\\d+(?=px)", "10px 2em 5px"))   # [\'10\', \'5\']\nprint(re.findall(r"(?<=\\$)\\d+", "$5 and 7"))      # [\'5\']\nprint(re.findall(r"\\b\\d+\\b(?!%)", "5% 7"))        # [\'7\']',
    'print(re.sub(r"\\s+", " ", "a  b\\n c"))                  # a b c\nprint(re.sub(r"(\\w+)-(\\w+)", r"\\2-\\1", "a-b"))          # b-a\nprint(re.sub(r"(?P<d>\\d)", r"[\\g<d>]", "a1"))           # a[1]\nprint(re.sub(r"\\d", lambda m: m[0] * 2, "a1"))          # a11\nprint(re.sub(r"o", "0", "foo", count=1))                # f0o',
    'print(re.findall(r"py", "Py PY", re.I))    # [\'Py\', \'PY\']\nprint(re.findall(r"(?i)py", "Py"))         # [\'Py\']\nprint(re.findall(r"a.b", "a\\nb", re.S))    # [\'a\\nb\']\n\nPHONE = re.compile(r"""\n    (?P<area>\\d{3}) -   # area code\n    (?P<num>\\d{4})      # local number\n""", re.X)\nprint(PHONE.search("dial 415-9000").groupdict())  # {\'area\': \'415\', \'num\': \'9000\'}',
    'print(re.findall(r"[\\w.+-]+@[\\w-]+\\.[\\w.]+", s))        # [\'ada@dev.io\']\nprint(re.findall(r"-?\\d+(?:\\.\\d+)?", "-3.5 2"))          # [\'-3.5\', \'2\']\nprint(re.findall(r"\\d{4}-\\d{2}-\\d{2}", s))               # [\'2026-03-14\']\nprint(re.findall(r"https?://\\S+", "x http://a"))         # [\'http://a\']\nprint(re.findall(r"#[0-9A-Fa-f]{6}", "red #FF0000"))     # [\'#FF0000\']\nprint(re.sub(r"<[^>]+>", "", "<b>hi</b>"))               # hi',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

Using match to search. re.match() only tries the start of the string. re.match(r"\d+", s) is None because s starts with "Order". Use re.search().

An unescaped dot. . matches any character, so r"\d+.\d+" also matches "1x5". Write \. for a real dot.

Groups change what findall returns. With one group, you get only the group's text:

python
print(re.findall(r"(\d+)px", "5px 9px"))   # ['5', '9']
print(re.split(r"(,)", "a,b"))             # ['a', ',', 'b']
+ setup added so this can run · re-runs 14 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

re = _AutoMock('re')
s = _AutoMock('s')
m = _AutoMock('m')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
    'print(re.findall(r"<.+>", "<a><b>"))    # [\'<a><b>\']\nprint(re.findall(r"<.+?>", "<a><b>"))   # [\'<a>\', \'<b>\']',
    'print(bool(re.search(r"^\\d+$", "2026")))               # True\nprint(re.findall(r"\\bcat\\b", "cat concat cat."))        # [\'cat\', \'cat\']\nprint(re.findall(r"^\\w+", "a b\\nc d", re.M))            # [\'a\', \'c\']',
    'm = re.search(r"(\\w+)@(\\S+)", s)\nprint(m.group(1), m.group(2))   # ada dev.io\nprint(m.group(0))               # ada@dev.io\n\nprint(re.findall(r"(\\d+)-(\\d+)", "1-2 3-4"))      # [(\'1\', \'2\'), (\'3\', \'4\')]\nprint(re.findall(r"(?:ab)+", "abab x ab"))        # [\'abab\', \'ab\']\nprint(re.findall(r"cat|dog", "cat, dog, cow"))    # [\'cat\', \'dog\']\nprint(re.findall(r"(\\w)\\1", "book keeper"))       # [\'o\', \'e\']',
    'm = re.search(r"(?P<y>\\d+)-(?P<m>\\d+)", s)\nprint(m["y"])            # 2026\nprint(m.group("y"))      # 2026\nprint(m.groupdict())     # {\'y\': \'2026\', \'m\': \'03\'}',
    'print(re.findall(r"\\d+(?=px)", "10px 2em 5px"))   # [\'10\', \'5\']\nprint(re.findall(r"(?<=\\$)\\d+", "$5 and 7"))      # [\'5\']\nprint(re.findall(r"\\b\\d+\\b(?!%)", "5% 7"))        # [\'7\']',
    'print(re.sub(r"\\s+", " ", "a  b\\n c"))                  # a b c\nprint(re.sub(r"(\\w+)-(\\w+)", r"\\2-\\1", "a-b"))          # b-a\nprint(re.sub(r"(?P<d>\\d)", r"[\\g<d>]", "a1"))           # a[1]\nprint(re.sub(r"\\d", lambda m: m[0] * 2, "a1"))          # a11\nprint(re.sub(r"o", "0", "foo", count=1))                # f0o',
    'print(re.findall(r"py", "Py PY", re.I))    # [\'Py\', \'PY\']\nprint(re.findall(r"(?i)py", "Py"))         # [\'Py\']\nprint(re.findall(r"a.b", "a\\nb", re.S))    # [\'a\\nb\']\n\nPHONE = re.compile(r"""\n    (?P<area>\\d{3}) -   # area code\n    (?P<num>\\d{4})      # local number\n""", re.X)\nprint(PHONE.search("dial 415-9000").groupdict())  # {\'area\': \'415\', \'num\': \'9000\'}',
    'print(re.findall(r"[\\w.+-]+@[\\w-]+\\.[\\w.]+", s))        # [\'ada@dev.io\']\nprint(re.findall(r"-?\\d+(?:\\.\\d+)?", "-3.5 2"))          # [\'-3.5\', \'2\']\nprint(re.findall(r"\\d{4}-\\d{2}-\\d{2}", s))               # [\'2026-03-14\']\nprint(re.findall(r"https?://\\S+", "x http://a"))         # [\'http://a\']\nprint(re.findall(r"#[0-9A-Fa-f]{6}", "red #FF0000"))     # [\'#FF0000\']\nprint(re.sub(r"<[^>]+>", "", "<b>hi</b>"))               # hi',
    'if m := re.search(r"\\d+", s):\n    print(m.group())   # 66',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

A group in split() keeps the separators in the result.

User text with special characters. In a pattern, + means "one or more". To search for text exactly as the user typed it, use re.escape():

python
print(re.findall(re.escape("a+b"), "a+b"))   # ['a+b']
+ setup added so this can run · re-runs 15 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

re = _AutoMock('re')
s = _AutoMock('s')
m = _AutoMock('m')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
    'print(re.findall(r"<.+>", "<a><b>"))    # [\'<a><b>\']\nprint(re.findall(r"<.+?>", "<a><b>"))   # [\'<a>\', \'<b>\']',
    'print(bool(re.search(r"^\\d+$", "2026")))               # True\nprint(re.findall(r"\\bcat\\b", "cat concat cat."))        # [\'cat\', \'cat\']\nprint(re.findall(r"^\\w+", "a b\\nc d", re.M))            # [\'a\', \'c\']',
    'm = re.search(r"(\\w+)@(\\S+)", s)\nprint(m.group(1), m.group(2))   # ada dev.io\nprint(m.group(0))               # ada@dev.io\n\nprint(re.findall(r"(\\d+)-(\\d+)", "1-2 3-4"))      # [(\'1\', \'2\'), (\'3\', \'4\')]\nprint(re.findall(r"(?:ab)+", "abab x ab"))        # [\'abab\', \'ab\']\nprint(re.findall(r"cat|dog", "cat, dog, cow"))    # [\'cat\', \'dog\']\nprint(re.findall(r"(\\w)\\1", "book keeper"))       # [\'o\', \'e\']',
    'm = re.search(r"(?P<y>\\d+)-(?P<m>\\d+)", s)\nprint(m["y"])            # 2026\nprint(m.group("y"))      # 2026\nprint(m.groupdict())     # {\'y\': \'2026\', \'m\': \'03\'}',
    'print(re.findall(r"\\d+(?=px)", "10px 2em 5px"))   # [\'10\', \'5\']\nprint(re.findall(r"(?<=\\$)\\d+", "$5 and 7"))      # [\'5\']\nprint(re.findall(r"\\b\\d+\\b(?!%)", "5% 7"))        # [\'7\']',
    'print(re.sub(r"\\s+", " ", "a  b\\n c"))                  # a b c\nprint(re.sub(r"(\\w+)-(\\w+)", r"\\2-\\1", "a-b"))          # b-a\nprint(re.sub(r"(?P<d>\\d)", r"[\\g<d>]", "a1"))           # a[1]\nprint(re.sub(r"\\d", lambda m: m[0] * 2, "a1"))          # a11\nprint(re.sub(r"o", "0", "foo", count=1))                # f0o',
    'print(re.findall(r"py", "Py PY", re.I))    # [\'Py\', \'PY\']\nprint(re.findall(r"(?i)py", "Py"))         # [\'Py\']\nprint(re.findall(r"a.b", "a\\nb", re.S))    # [\'a\\nb\']\n\nPHONE = re.compile(r"""\n    (?P<area>\\d{3}) -   # area code\n    (?P<num>\\d{4})      # local number\n""", re.X)\nprint(PHONE.search("dial 415-9000").groupdict())  # {\'area\': \'415\', \'num\': \'9000\'}',
    'print(re.findall(r"[\\w.+-]+@[\\w-]+\\.[\\w.]+", s))        # [\'ada@dev.io\']\nprint(re.findall(r"-?\\d+(?:\\.\\d+)?", "-3.5 2"))          # [\'-3.5\', \'2\']\nprint(re.findall(r"\\d{4}-\\d{2}-\\d{2}", s))               # [\'2026-03-14\']\nprint(re.findall(r"https?://\\S+", "x http://a"))         # [\'http://a\']\nprint(re.findall(r"#[0-9A-Fa-f]{6}", "red #FF0000"))     # [\'#FF0000\']\nprint(re.sub(r"<[^>]+>", "", "<b>hi</b>"))               # hi',
    'if m := re.search(r"\\d+", s):\n    print(m.group())   # 66',
    'print(re.findall(r"(\\d+)px", "5px 9px"))   # [\'5\', \'9\']\nprint(re.split(r"(,)", "a,b"))             # [\'a\', \',\', \'b\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

Forgetting the r. In "\bhi" without r, Python turns \b into a backspace character, so the pattern finds nothing. Python 3.12 and later also warn about other invalid escapes such as "\d". Always write r"...".

[A-z] instead of [A-Za-z]. The range from A to z also includes [ \ ] ^ _ and the backtick:

python
print(re.findall(r"[A-z]", "^_"))   # ['^', '_']
+ setup added so this can run · re-runs 16 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

re = _AutoMock('re')
s = _AutoMock('s')
m = _AutoMock('m')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    import re
except Exception:
    pass
_pm_earlier = [
    'import re\n\ns = "Order 66 shipped 2026-03-14 to ada@dev.io"',
    'print(re.findall(r"\\d+", s))                # [\'66\', \'2026\', \'03\', \'14\']\nprint(re.search(r"\\d+", s).group())         # 66\nprint(re.match(r"\\d+", s))                  # None\nprint(bool(re.fullmatch(r"\\d{4}", "2026"))) # True\nprint(re.sub(r"\\d", "#", "a1b22"))          # a#b##\nprint(re.split(r"[,;]\\s*", "a, b;c"))       # [\'a\', \'b\', \'c\']\nprint([m.span() for m in re.finditer(r"\\d+", "a1 b22")])  # [(1, 2), (4, 6)]',
    'DATE = re.compile(r"\\d{4}-\\d\\d-\\d\\d")\nprint(DATE.search(s).group())   # 2026-03-14',
    'print(re.findall(r"\\w+", "it\'s ok_2"))       # [\'it\', \'s\', \'ok_2\']\nprint(re.findall(r"[aeiou]", "regex"))       # [\'e\', \'e\']\nprint(re.findall(r"[^a-z ]", "ab C1"))       # [\'C\', \'1\']\nprint(re.findall(r"a.c", "abc a-c ac"))      # [\'abc\', \'a-c\']\nprint(re.findall(r"\\.", "v1.2.3"))           # [\'.\', \'.\']',
    'print(re.findall(r"lo+l", "ll lol loool"))        # [\'lol\', \'loool\']\nprint(re.findall(r"lo*l", "ll lol"))              # [\'ll\', \'lol\']\nprint(re.findall(r"colou?r", "color colour"))     # [\'color\', \'colour\']\nprint(re.findall(r"\\d{3}", "12345678"))           # [\'123\', \'456\']\nprint(re.findall(r"\\d{2,3}", "12345678"))         # [\'123\', \'456\', \'78\']',
    'print(re.findall(r"<.+>", "<a><b>"))    # [\'<a><b>\']\nprint(re.findall(r"<.+?>", "<a><b>"))   # [\'<a>\', \'<b>\']',
    'print(bool(re.search(r"^\\d+$", "2026")))               # True\nprint(re.findall(r"\\bcat\\b", "cat concat cat."))        # [\'cat\', \'cat\']\nprint(re.findall(r"^\\w+", "a b\\nc d", re.M))            # [\'a\', \'c\']',
    'm = re.search(r"(\\w+)@(\\S+)", s)\nprint(m.group(1), m.group(2))   # ada dev.io\nprint(m.group(0))               # ada@dev.io\n\nprint(re.findall(r"(\\d+)-(\\d+)", "1-2 3-4"))      # [(\'1\', \'2\'), (\'3\', \'4\')]\nprint(re.findall(r"(?:ab)+", "abab x ab"))        # [\'abab\', \'ab\']\nprint(re.findall(r"cat|dog", "cat, dog, cow"))    # [\'cat\', \'dog\']\nprint(re.findall(r"(\\w)\\1", "book keeper"))       # [\'o\', \'e\']',
    'm = re.search(r"(?P<y>\\d+)-(?P<m>\\d+)", s)\nprint(m["y"])            # 2026\nprint(m.group("y"))      # 2026\nprint(m.groupdict())     # {\'y\': \'2026\', \'m\': \'03\'}',
    'print(re.findall(r"\\d+(?=px)", "10px 2em 5px"))   # [\'10\', \'5\']\nprint(re.findall(r"(?<=\\$)\\d+", "$5 and 7"))      # [\'5\']\nprint(re.findall(r"\\b\\d+\\b(?!%)", "5% 7"))        # [\'7\']',
    'print(re.sub(r"\\s+", " ", "a  b\\n c"))                  # a b c\nprint(re.sub(r"(\\w+)-(\\w+)", r"\\2-\\1", "a-b"))          # b-a\nprint(re.sub(r"(?P<d>\\d)", r"[\\g<d>]", "a1"))           # a[1]\nprint(re.sub(r"\\d", lambda m: m[0] * 2, "a1"))          # a11\nprint(re.sub(r"o", "0", "foo", count=1))                # f0o',
    'print(re.findall(r"py", "Py PY", re.I))    # [\'Py\', \'PY\']\nprint(re.findall(r"(?i)py", "Py"))         # [\'Py\']\nprint(re.findall(r"a.b", "a\\nb", re.S))    # [\'a\\nb\']\n\nPHONE = re.compile(r"""\n    (?P<area>\\d{3}) -   # area code\n    (?P<num>\\d{4})      # local number\n""", re.X)\nprint(PHONE.search("dial 415-9000").groupdict())  # {\'area\': \'415\', \'num\': \'9000\'}',
    'print(re.findall(r"[\\w.+-]+@[\\w-]+\\.[\\w.]+", s))        # [\'ada@dev.io\']\nprint(re.findall(r"-?\\d+(?:\\.\\d+)?", "-3.5 2"))          # [\'-3.5\', \'2\']\nprint(re.findall(r"\\d{4}-\\d{2}-\\d{2}", s))               # [\'2026-03-14\']\nprint(re.findall(r"https?://\\S+", "x http://a"))         # [\'http://a\']\nprint(re.findall(r"#[0-9A-Fa-f]{6}", "red #FF0000"))     # [\'#FF0000\']\nprint(re.sub(r"<[^>]+>", "", "<b>hi</b>"))               # hi',
    'if m := re.search(r"\\d+", s):\n    print(m.group())   # 66',
    'print(re.findall(r"(\\d+)px", "5px 9px"))   # [\'5\', \'9\']\nprint(re.split(r"(,)", "a,b"))             # [\'a\', \',\', \'b\']',
    'print(re.findall(re.escape("a+b"), "a+b"))   # [\'a+b\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

Very slow patterns. Nested repeats such as (a+)+ can take an extremely long time on some inputs. Keep patterns simple, and avoid a repeat inside a repeat.

See also

Practice this

on practicepython.in

Short exercises that run in your browser and tell you what your code did, as well as whether a test passed.

Found this useful? Share it with a friend who’s learning Python.