Tuesday, September 8, 2026
HomeData ScienceRegular Expressions in Python – Complete re Module Guide 2026

Regular Expressions in Python – Complete re Module Guide 2026

Table of Content

Regular expressions (regex) are a mini-language for pattern matching in text. They are indispensable for data cleaning, log parsing, form validation, and text extraction. Python’s built-in re module provides a full regex engine. This guide covers everything from basic patterns to advanced lookaheads, with real-world data science examples throughout.

Core re Functions

import re

text = "DataExpertise published 42 articles in 2025 and 58 articles in 2026."

# re.search — find first match anywhere in string
m = re.search(r'\d+', text)
print(m.group())        # '42'
print(m.start(), m.end())  # 24 26

# re.match — match only at the START of string
m = re.match(r'Data', text)  # matches
m = re.match(r'\d+', text)   # None — doesn't start with digit

# re.fullmatch — entire string must match
re.fullmatch(r'\d{4}', '2026')    # matches
re.fullmatch(r'\d{4}', '2026x')   # None

# re.findall — return list of all matches
numbers = re.findall(r'\d+', text)
print(numbers)   # ['42', '2025', '58', '2026']

# re.finditer — return iterator of match objects
for m in re.finditer(r'\d+', text):
    print(f'Found {m.group()} at position {m.start()}-{m.end()}')

# re.sub — replace matches
clean = re.sub(r'\d+', 'N', text)
print(clean)   # 'DataExpertise published N articles in N and N articles in N.'

# re.split — split on pattern
parts = re.split(r'\s+', 'Hello   World	Python')
print(parts)   # ['Hello', 'World', 'Python']

Pattern Syntax Reference

Lines of colorful JavaScript code displayed on a dark screen
Photo by Markus Spiske on Unsplash
# Character classes
r'\d'    # any digit [0-9]
r'\D'    # any non-digit
r'\w'    # word character [a-zA-Z0-9_]
r'\W'    # non-word character
r'\s'    # whitespace (space, tab, newline)
r'\S'    # non-whitespace
r'.'     # any character except newline (with re.DOTALL it matches newline too)

# Quantifiers
r'a*'    # 0 or more a's (greedy)
r'a+'    # 1 or more a's (greedy)
r'a?'    # 0 or 1 a
r'a{3}'  # exactly 3 a's
r'a{2,5}'# 2 to 5 a's
r'a*?'   # 0 or more a's (non-greedy / lazy)
r'a+?'   # 1 or more a's (lazy)

# Anchors
r'^abc'  # abc at start of string (or start of line with re.MULTILINE)
r'abc$'  # abc at end of string
r'word'  # word boundary — matches 'word' but not 'wording'

# Alternation and grouping
r'cat|dog'         # cat or dog
r'(cat|dog)s?'     # cat or dog, optionally followed by s
r'(?:cat|dog)'     # non-capturing group

Groups and Named Groups

log = '2026-09-08 14:32:01 ERROR user_id=42 message=Connection timeout'

# Numbered groups
m = re.search(r'(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) (\w+)', log)
print(m.group(0))   # full match
print(m.group(1))   # '2026-09-08'
print(m.group(2))   # '14:32:01'
print(m.group(3))   # 'ERROR'
print(m.groups())   # ('2026-09-08', '14:32:01', 'ERROR')

# Named groups — more readable
pattern = r'(?P\d{4}-\d{2}-\d{2}) (?P

Lookaheads and Lookbehinds

a black and white photo with the words life looks better in color
Photo by Samuel Regan-Asante on Unsplash
text = 'Price: ₹1,250.00  USD: $99.99  EUR: €85.50'

# Positive lookahead (?=...) — match X only if followed by Y
prices_after_rupee = re.findall(r'(?<=₹)[\d,]+\.?\d*', text)
print(prices_after_rupee)   # ['1,250.00']

# Positive lookbehind (?<=...) — match X only if preceded by Y
dollars = re.findall(r'(?<=\$)[\d.]+', text)
print(dollars)   # ['99.99']

# Negative lookahead (?!...) — match X only if NOT followed by Y
words_not_followed_by_comma = re.findall(r'\w+(?!,)', 'cat, dog, bird fish')

# Extract prices regardless of currency symbol
all_prices = re.findall(r'(?<=[\$₹€])[\d,.]+', text)
print(all_prices)   # ['1,250.00', '99.99', '85.50']

Compiled Patterns for Performance

# Compile once, use many times — 10-50x faster for large datasets
email_re = re.compile(
    r'^[\w.+\-]+@[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}$'
)
phone_re = re.compile(r'(?:\+91|0)?[6-9]\d{9}')

# Apply to a pandas column (use str.contains or apply)
import pandas as pd

df = pd.DataFrame({'email': ['user@example.com', 'bad-email', 'hello@data.in']})
df['valid_email'] = df['email'].str.match(r'^[\w.+\-]+@[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}$')

# Vectorised — fastest for large DataFrames
df['has_digit'] = df['email'].str.contains(r'\d', regex=True)

# Extract groups with str.extract
df2 = pd.DataFrame({'log': ['ERROR: disk full', 'INFO: started', 'WARN: low memory']})
df2[['level', 'msg']] = df2['log'].str.extract(r'^(\w+): (.+)$')
print(df2)

Real-World Patterns

# Email
r'^[\w.+\-]+@[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}$'

# Indian mobile number
r'(?:\+91|0)?[6-9]\d{9}'

# URL
r'https?://(?:www\.)?[-\w@:%._+~#=]{1,256}\.[a-zA-Z]{2,6}(?:[-\w@:%_+.~#?&/=]*)'

# IPv4 address
r'(?:\d{1,3}\.){3}\d{1,3}'

# Date YYYY-MM-DD
r'\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])'

# HTML tag stripper
re.sub(r'<[^>]+>', '', html_content)

# Remove extra whitespace
re.sub(r'\s+', ' ', text).strip()

# Extract JSON-like key-value from logs
re.findall(r'(\w+)=([^\s,]+)', log_line)

# Find all words that appear in ALL CAPS
re.findall(r'[A-Z]{2,}', text)

# Remove non-ASCII characters
re.sub(r'[^-]+', '', text)

Flags

# re.IGNORECASE — case-insensitive matching
re.findall(r'python', text, flags=re.IGNORECASE)

# re.MULTILINE — ^ and $ match start/end of each line
re.findall(r'^\w+', multiline_text, flags=re.MULTILINE)

# re.DOTALL — . matches newline too
re.search(r'
.+?
', html, flags=re.DOTALL) # re.VERBOSE — write readable patterns with comments email_pattern = re.compile(r''' ^ # start of string [\w.+\-]+ # username: word chars, dots, plus, hyphen @ # literal @ [a-zA-Z0-9\-]+ # domain name \. # literal dot [a-zA-Z]{2,} # TLD (2+ letters) $ # end of string ''', re.VERBOSE | re.IGNORECASE)

Conclusion

Regular expressions reward time invested in learning them — even basic knowledge of \d, \w, \s, and the quantifiers solves the majority of text cleaning tasks. Use re.compile() for patterns you apply many times. Prefer non-capturing groups (?:...) when you do not need to extract sub-matches. Use named groups when a pattern has many captures. Test your patterns interactively on regex101.com before embedding them in production code — it visualises what each part matches, which saves hours of debugging.

Leave feedback about this

  • Rating

Durgesh Kekare
Durgesh Kekarehttps://www.dataexpertise.in
Durgesh Kekare is a data science educator and founder of DataExpertise.in. With expertise in Python, machine learning, and analytics, he helps 10,000+ learners break into data careers.

Latest Posts

List of Categories