Regular Expressions

Difficulty: Intermediate

Question

How do you use regular expressions in Python? Explain common patterns.

Answer

Python's re module provides regex support for pattern matching in strings.

Key functions: - re.search(): Find first match anywhere in string - re.match(): Match only at the beginning - re.findall(): Find all matches, return as list - re.finditer(): Find all matches, return as iterator of Match objects - re.sub(): Replace matches - re.compile(): Pre-compile pattern for reuse

Common patterns: \d (digit), \w (word char), \s (whitespace), . (any char), * (0+), + (1+), ? (0 or 1), {n,m} (n to m), [] (character class), () (group).

Code examples

Basic Pattern Matching

import re

text = "Contact us at support@example.com or sales@company.org"

# search: first match
match = re.search(r'\w+@\w+\.\w+', text)
if match:
    print(f"Found: {match.group()}")  # support@example.com
    print(f"Position: {match.start()}-{match.end()}")

# findall: all matches
emails = re.findall(r'\w+@\w+\.\w+', text)
print(emails)  # ['support@example.com', 'sales@company.org']

# match: only at beginning
print(re.match(r'Contact', text))  # Match object
print(re.match(r'support', text))  # None (not at start)

# Common patterns
phone = "Call 123-456-7890 or (987) 654-3210"
phones = re.findall(r'\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{4}', phone)
print(phones)  # ['123-456-7890', '(987) 654-3210']

Use raw strings (r'...') for regex to avoid double-escaping backslashes. search() finds the first match; findall() finds all non-overlapping matches.

Groups and Named Groups

import re

# Capturing groups with ()
date_text = "Today is 2024-03-15 and tomorrow is 2024-03-16"
pattern = r'(\d{4})-(\d{2})-(\d{2})'

match = re.search(pattern, date_text)
if match:
    print(f"Full match: {match.group(0)}")  # 2024-03-15
    print(f"Year: {match.group(1)}")        # 2024
    print(f"Month: {match.group(2)}")       # 2024
    print(f"Day: {match.group(3)}")         # 15

# Named groups (?P<name>...)
pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
for match in re.finditer(pattern, date_text):
    print(f"{match.group('year')}/{match.group('month')}/{match.group('day')}")

# findall with groups returns tuples
dates = re.findall(pattern, date_text)
print(dates)  # [('2024', '03', '15'), ('2024', '03', '16')]

# Non-capturing group (?:...)
words = re.findall(r'(?:Mr|Mrs|Ms)\.\s(\w+)', "Mr. Smith and Mrs. Jones")
print(words)  # ['Smith', 'Jones'] - only captures the name

Groups capture parts of the match. Named groups (?P<name>...) make regex more readable. Non-capturing groups (?:...) group without capturing.

Substitution and Compilation

import re

# re.sub: find and replace
text = "Hello World, Hello Python"
result = re.sub(r'Hello', 'Hi', text)
print(result)  # 'Hi World, Hi Python'

# Substitution with function
def censor(match):
    word = match.group()
    return word[0] + '*' * (len(word) - 1)

text = "The password is secret123"
result = re.sub(r'\b\w*\d+\w*\b', censor, text)
print(result)  # 'The password is s*'

# Compile for reuse (better performance in loops)
email_pattern = re.compile(
    r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
#39; ) emails = ['user@example.com', 'bad@', 'test@site.org', 'invalid'] for email in emails: if email_pattern.match(email): print(f"Valid: {email}") else: print(f"Invalid: {email}") # Flags text = "Hello\nhello\nHELLO" matches = re.findall(r'hello', text, re.IGNORECASE) print(matches) # ['Hello', 'hello', 'HELLO']

re.compile() pre-compiles the pattern for better performance when reused. re.sub() can take a function as replacement for dynamic substitution.

Key points

Concepts covered

re module, Pattern Matching, Groups, Substitution, Compilation