Python Tutorial › Modules › RegEx

Python RegEx

IntermediateLesson 40 of 4714 min read12 runnable examples

A regular expression (regex) is a small pattern language for finding and extracting text: every email address in a page, every number in a log, every date in a report. Python’s re module runs these patterns for you.

Advertisement
In this lesson
  • search, match, fullmatch, findall and sub
  • Character classes and quantifiers
  • Capture groups and named groups
  • Flags and compiled patterns

Your first regex

Write patterns as raw strings (r"...") so backslashes reach the regex engine unchanged.

Python
import re text = "Order 4521 shipped on 2026-11-02, order 4522 pending"match = re.search(r"\d+", text)      # first run of digitsprint(match.group(), match.start(), match.end()) print(re.findall(r"\d+", text))     # every run of digits
Output
4521 6 10
['4521', '2026', '11', '02', '4522']

The main functions

FunctionReturns
re.search(p, s)First match anywhere, or None
re.match(p, s)Match only at the start, or None
re.fullmatch(p, s)Match only if the whole string fits
re.findall(p, s)List of all matches (or of groups)
re.finditer(p, s)Iterator of match objects
re.sub(p, repl, s)String with matches replaced
re.split(p, s)List split on the pattern
Python
import re print(re.match(r"\d+", "123abc"))print(re.match(r"\d+", "abc123"))print(bool(re.fullmatch(r"\d{6}", "411001")), bool(re.fullmatch(r"\d{6}", "41100")))print(re.sub(r"\s+", " ", "too    many   spaces"))print(re.split(r"[,;]\s*", "a, b;c ,d"))
Output
<re.Match object; span=(0, 3), match='123'>
None
True False
too many spaces
['a', 'b', 'c ', 'd']
Tip

Always check for None before calling .group() on the result of search or match: if m := re.search(p, s): print(m.group()).

Character classes

PatternMatches
\d / \Da digit / not a digit
\w / \Wa word character (letter, digit, _) / not
\s / \Swhitespace / not
.any character except newline
[aeiou]one of the listed characters
[a-z0-9]a character in the ranges
[^0-9]any character NOT listed
Python
import re s = "Room B-12, floor 3; call +91 98765 43210"print(re.findall(r"[A-Z]", s))print(re.findall(r"[aeiou]", s))print(re.findall(r"[^\w\s]", s))
Output
['R', 'B']
['o', 'o', 'o', 'o', 'a']
['-', ',', ';', '+']

Quantifiers

PatternMeaning
*0 or more
+1 or more
?0 or 1 (optional)
{3}exactly 3
{2,4}2 to 4
Python
import re print(re.findall(r"colou?r", "color colour colr"))print(re.findall(r"\d{2,3}", "1 12 123 1234"))print(re.findall(r"go+gle", "ggle gogle google gooogle"))
Output
['color', 'colour']
['12', '123', '123']
['gogle', 'google', 'gooogle']

Quantifiers are greedy: they take as much as possible. Add ? to make them lazy:

Python
import re html = "<b>bold</b> and <i>italic</i>"print(re.findall(r"<.+>", html))     # greedy: one huge matchprint(re.findall(r"<.+?>", html))    # lazy: each tag
Output
['<b>bold</b> and <i>italic</i>']
['<b>', '</b>', '<i>', '</i>']

Anchors and boundaries

^ is the start, $ the end, and \b a word boundary:

Python
import re print(re.findall(r"\bcat\b", "cat catalog concat cat."))print(bool(re.search(r"^ERROR", "ERROR: disk full")), bool(re.search(r"\.py$", "test_login.py")))
Output
['cat', 'cat']
True True

Groups

Brackets capture part of the match. findall returns the groups when the pattern has them.

Python
import re log = "2026-11-02 ERROR db; 2026-11-03 WARN cache"for m in re.finditer(r"(\d{4})-(\d{2})-(\d{2}) (\w+)", log):    print(m.group(0), "->", m.group(1), m.group(4)) print(re.findall(r"(\d{4})-\d{2}-\d{2} (\w+)", log))
Output
2026-11-02 ERROR -> 2026 ERROR
2026-11-03 WARN -> 2026 WARN
[('2026', 'ERROR'), ('2026', 'WARN')]

Named groups make patterns self-documenting:

Python
import re m = re.search(r"(?P<user>[\w.]+)@(?P<domain>[\w.-]+)", "contact: asha.k@sqatools.in today")print(m.group("user"), m.group("domain"))print(m.groupdict())
Output
asha.k sqatools.in
{'user': 'asha.k', 'domain': 'sqatools.in'}

Use groups in a replacement with \1 or \g<name>:

Python
import reprint(re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3/\2/\1", "Due 2026-11-02 and 2026-12-25"))
Output
Due 02/11/2026 and 25/12/2026

Flags

Python
import re text = "Python\npython\nPYTHON"print(re.findall(r"python", text, flags=re.IGNORECASE))print(re.findall(r"^p\w+", text, flags=re.I | re.M))     # ^ at every line start
Output
['Python', 'python', 'PYTHON']
['Python', 'python', 'PYTHON']

Compile reusable patterns

Python
import re EMAIL = re.compile(r"^[\w.+-]+@[\w-]+(\.[\w-]+)+$")for e in ["asha@sqatools.in", "bad@", "ravi.k+test@mail.co.uk", "no spaces@x.com"]:    print(e, bool(EMAIL.match(e)))
Output
asha@sqatools.in True
bad@ False
ravi.k+test@mail.co.uk True
no spaces@x.com False
Watch out

Regex is great for patterns in text, but do not use it to parse HTML or JSON. Use html.parser, BeautifulSoup or json instead.

Escape special characters

Characters like . * + ? ( ) [ ] { } ^ $ | and the backslash have special meanings. Escape them with a backslash, or let re.escape() do it:

Python
import re price = "Rs. 1,499.50 (incl. tax)"print(re.findall(r"\d[\d,]*\.\d+", price))print(re.escape("1+1=2?"))
Output
['1,499.50']
1\+1=2\?

Check your understanding

Predict the output

import res = "a1b22c333"print(re.findall(r"\d", s), re.findall(r"\d+", s), re.sub(r"\d+", "#", s))

Quick quiz

1. What is the difference between re.match and re.search?

2. What does \d{3} match?

3. Why write patterns as raw strings like r"\d+"?

4. What does re.findall return when the pattern has one group?

Exercise: extract phone numbers

Find all 10-digit Indian mobile numbers (starting with 6, 7, 8 or 9) in the text, allowing an optional +91 and a space or hyphen after the first 5 digits.

Input
"Call 98765-43210 or +91 91234 56789; old: 12345 67890"
Expected output
98765-43210 -> 9876543210
+91 91234 56789 -> 9123456789
Hint 1

Start with (?:\+91\s?)? for the optional country code; (?: ) groups without capturing.

Hint 2

Then [6-9]\d{4}[\s-]?\d{5}.

Program Practice

Python regex FAQ

What is the re module in Python?

re is the standard-library module for regular expressions. It provides search, match, findall, sub, split and compile.

How do I extract numbers from a string in Python?

re.findall(r"\d+", text) returns every run of digits as strings. Use r"-?\d+(?:\.\d+)?" to include negatives and decimals.

What does re.compile do?

It turns a pattern into a reusable object. It makes code clearer when the same pattern is used many times; Python also caches recently used patterns automatically.

How do I make a regex case-insensitive?

Pass flags=re.IGNORECASE (or re.I), or start the pattern with (?i).

Leave a Comment