Validating email addresses, parsing log files, and extracting data from text are tedious with basic string methods. Regular expressions provide a powerful pattern language for matching, searching, and manipulating text. Python's re module makes these patterns accessible for validation, extraction, and text transformation.

Regular expressions (regex) are patterns used to match character combinations in strings. They provide a powerful way to search, validate, and manipulate text.

Literal Matching

text1
literal.py
Replay: real traced execution (multi-file project)
# Literal matching

import re

# Basic pattern matching
# Literal match
text1 = "hello"
match1 = re.match(r"hello", text1)
print(f"'hello' matches 'hello': {match1 is not None}")

match2 = re.match(r"world", text1)
print(f"'hello' matches 'world': {match2 is not None}")

# Full match vs partial match
print("\nFull match vs search:")
print(f"Full match 'hello' in 'hello world': {re.fullmatch(r'hello', 'hello world') is not None}")
print(f"Search 'hello' in 'hello world': {re.search(r'hello', 'hello world') is not None}")

# Compiled pattern
pattern = re.compile(r"hello")
match3 = pattern.search("hello world")
print(f"\nCompiled pattern found: {match3 is not None}")

# Case sensitivity
match4 = re.match(r"hello", "Hello")
print(f"\n'Hello' matches 'hello': {match4 is not None}")

# Case insensitive
match5 = re.match(r"hello", "Hello", re.IGNORECASE)
print(f"Case insensitive match: {match5 is not None}")

# Multiple occurrences
text2 = "hello hello hello"
matches = re.finditer(r"hello", text2)

print("\nFind all occurrences:")
for match in matches:
    print(f"  Found at index: {match.start()}")

# Match object details
text3 = "hello world"
match6 = re.search(r"hello", text3)
if match6:
    print(f"\nMatch details:")
    print(f"  Matched text: {match6.group()}")
    print(f"  Start: {match6.start()}, End: {match6.end()}")
    print(f"  Span: {match6.span()}")

# Literal matching

import re

# Basic pattern matching
# Literal match
text1 = "Hello"
match1 = re.match(r"hello", text1)
print(f"'hello' matches 'hello': {match1 is not None}")

match2 = re.match(r"world", text1)
print(f"'hello' matches 'world': {match2 is not None}")

# Full match vs partial match
print("\nFull match vs search:")
print(f"Full match 'hello' in 'hello world': {re.fullmatch(r'hello', 'hello world') is not None}")
print(f"Search 'hello' in 'hello world': {re.search(r'hello', 'hello world') is not None}")

# Compiled pattern
pattern = re.compile(r"hello")
match3 = pattern.search("hello world")
print(f"\nCompiled pattern found: {match3 is not None}")

# Case sensitivity
match4 = re.match(r"hello", "Hello")
print(f"\n'Hello' matches 'hello': {match4 is not None}")

# Case insensitive
match5 = re.match(r"hello", "Hello", re.IGNORECASE)
print(f"Case insensitive match: {match5 is not None}")

# Multiple occurrences
text2 = "hello hello hello"
matches = re.finditer(r"hello", text2)

print("\nFind all occurrences:")
for match in matches:
    print(f"  Found at index: {match.start()}")

# Match object details
text3 = "hello world"
match6 = re.search(r"hello", text3)
if match6:
    print(f"\nMatch details:")
    print(f"  Matched text: {match6.group()}")
    print(f"  Start: {match6.start()}, End: {match6.end()}")
    print(f"  Span: {match6.span()}")

# Literal matching

import re

# Basic pattern matching
# Literal match
text1 = "world"
match1 = re.match(r"hello", text1)
print(f"'hello' matches 'hello': {match1 is not None}")

match2 = re.match(r"world", text1)
print(f"'hello' matches 'world': {match2 is not None}")

# Full match vs partial match
print("\nFull match vs search:")
print(f"Full match 'hello' in 'hello world': {re.fullmatch(r'hello', 'hello world') is not None}")
print(f"Search 'hello' in 'hello world': {re.search(r'hello', 'hello world') is not None}")

# Compiled pattern
pattern = re.compile(r"hello")
match3 = pattern.search("hello world")
print(f"\nCompiled pattern found: {match3 is not None}")

# Case sensitivity
match4 = re.match(r"hello", "Hello")
print(f"\n'Hello' matches 'hello': {match4 is not None}")

# Case insensitive
match5 = re.match(r"hello", "Hello", re.IGNORECASE)
print(f"Case insensitive match: {match5 is not None}")

# Multiple occurrences
text2 = "hello hello hello"
matches = re.finditer(r"hello", text2)

print("\nFind all occurrences:")
for match in matches:
    print(f"  Found at index: {match.start()}")

# Match object details
text3 = "hello world"
match6 = re.search(r"hello", text3)
if match6:
    print(f"\nMatch details:")
    print(f"  Matched text: {match6.group()}")
    print(f"  Start: {match6.start()}, End: {match6.end()}")
    print(f"  Span: {match6.span()}")

  1. text1 ← hello, match1 ← <re.Match object; span=(0, 5), match='hello'>

    6# Literal match7text1→ hello = "hello"8#@text1="Hello", "world"9match1→ <re.Match object; span=(0, 5), match='hello'> = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"hello", text1hello)10print(f"'hello' matches 'hello': {match1<re.Match object; span=(0, 5), match='hello'> is not None}")1112match2→ None = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"world", text1hello)13print(f"'hello' matches 'world': {match2None is not None}")1415# Full match vs partial match16print("\nFull match vs search:")17print(f"Full match 'hello' in 'hello world': {re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r'hello', 'hello world') is not None}")18print(f"Search 'hello' in 'hello world': {re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r'hello', 'hello world') is not None}")1920# Compiled pattern21pattern→ re.compile('hello') = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.compile(r"hello")22match3→ <re.Match object; span=(0, 5), match='hello'> = patternre.compile('hello').search("hello world")23print(f"\nCompiled pattern found: {match3<re.Match object; span=(0, 5), match='hello'> is not None}")2425# Case sensitivity26match4→ None = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"hello", "Hello")27print(f"\n'Hello' matches 'hello': {match4None is not None}")2829# Case insensitive30match5→ <re.Match object; span=(0, 5), match='Hello'> = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"hello", "Hello", re.IGNORECASEre.IGNORECASE)31print(f"Case insensitive match: {match5<re.Match object; span=(0, 5), match='Hello'> is not None}")3233# Multiple occurrences34text2→ hello hello hello = "hello hello hello"35matches→ ⟨callable_iterator A⟩ = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.finditer(r"hello", text2hello hello hello)3637print("\nFind all occurrences:")38for match in matches:
    output'hello' matches 'hello': True
    'hello' matches 'world': False
    
    Full match vs search:
    Full match 'hello' in 'hello world': False
    Search 'hello' in 'hello world': True
    
    Compiled pattern found: True
    
    'Hello' matches 'hello': False
    Case insensitive match: True
    
    Find all occurrences:
  2. for match in matches:

    pass 1 of 3
    37print("\nFind all occurrences:")38for match<re.Match object; span=(0, 5), match='hello'> in matches⟨callable_iterator A⟩:39    print(f"  Found at index: {match<re.Match object; span=(0, 5), match='hello'>.start()}")
    output  Found at index: 0
    All 3 passes — pass 1 is the card above
    passmatch
    1<re.Match object; span=(0, 5), match='hello'>
    2<re.Match object; span=(6, 11), match='hello'>
    3<re.Match object; span=(12, 17), match='hello'>
  3. text3 ← hello world, match6 ← <re.Match object; span=(0, 5), match='hello'>

    41# Match object details42text3→ hello world = "hello world"43match6→ <re.Match object; span=(0, 5), match='hello'> = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"hello", text3hello world)44if match6:
  4. if match6:

    43match6 = re.search(r"hello", text3)44if match6<re.Match object; span=(0, 5), match='hello'>:45    print(f"\nMatch details:")46    print(f"  Matched text: {match6<re.Match object; span=(0, 5), match='hello'>.group()}")47    print(f"  Start: {match6<re.Match object; span=(0, 5), match='hello'>.start()}, End: {match6.end()}")48    print(f"  Span: {match6<re.Match object; span=(0, 5), match='hello'>.span()}")
    output
    Match details:
      Matched text: hello
      Start: 0, End: 5
      Span: (0, 5)
  1. text1 ← Hello, match1 ← None, match2 ← None, pattern ← re.compile('hello')

    6# Literal match7text1→ Hello = "Hello"8match1→ None = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"hello", text1Hello)9print(f"'hello' matches 'hello': {match1None is not None}")1011match2→ None = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"world", text1Hello)12print(f"'hello' matches 'world': {match2None is not None}")1314# Full match vs partial match15print("\nFull match vs search:")16print(f"Full match 'hello' in 'hello world': {re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r'hello', 'hello world') is not None}")17print(f"Search 'hello' in 'hello world': {re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r'hello', 'hello world') is not None}")1819# Compiled pattern20pattern→ re.compile('hello') = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.compile(r"hello")21match3→ <re.Match object; span=(0, 5), match='hello'> = patternre.compile('hello').search("hello world")22print(f"\nCompiled pattern found: {match3<re.Match object; span=(0, 5), match='hello'> is not None}")2324# Case sensitivity25match4→ None = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"hello", "Hello")26print(f"\n'Hello' matches 'hello': {match4None is not None}")2728# Case insensitive29match5→ <re.Match object; span=(0, 5), match='Hello'> = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"hello", "Hello", re.IGNORECASEre.IGNORECASE)30print(f"Case insensitive match: {match5<re.Match object; span=(0, 5), match='Hello'> is not None}")3132# Multiple occurrences33text2→ hello hello hello = "hello hello hello"34matches→ ⟨callable_iterator A⟩ = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.finditer(r"hello", text2hello hello hello)3536print("\nFind all occurrences:")37for match in matches:
    output'hello' matches 'hello': False
    'hello' matches 'world': False
    
    Full match vs search:
    Full match 'hello' in 'hello world': False
    Search 'hello' in 'hello world': True
    
    Compiled pattern found: True
    
    'Hello' matches 'hello': False
    Case insensitive match: True
    
    Find all occurrences:
  2. for match in matches:

    pass 1 of 3
    36print("\nFind all occurrences:")37for match<re.Match object; span=(0, 5), match='hello'> in matches⟨callable_iterator A⟩:38    print(f"  Found at index: {match<re.Match object; span=(0, 5), match='hello'>.start()}")
    output  Found at index: 0
    All 3 passes — pass 1 is the card above
    passmatch
    1<re.Match object; span=(0, 5), match='hello'>
    2<re.Match object; span=(6, 11), match='hello'>
    3<re.Match object; span=(12, 17), match='hello'>
  3. text3 ← hello world, match6 ← <re.Match object; span=(0, 5), match='hello'>

    40# Match object details41text3→ hello world = "hello world"42match6→ <re.Match object; span=(0, 5), match='hello'> = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"hello", text3hello world)43if match6:
  4. if match6:

    42match6 = re.search(r"hello", text3)43if match6<re.Match object; span=(0, 5), match='hello'>:44    print(f"\nMatch details:")45    print(f"  Matched text: {match6<re.Match object; span=(0, 5), match='hello'>.group()}")46    print(f"  Start: {match6<re.Match object; span=(0, 5), match='hello'>.start()}, End: {match6.end()}")47    print(f"  Span: {match6<re.Match object; span=(0, 5), match='hello'>.span()}")
    output
    Match details:
      Matched text: hello
      Start: 0, End: 5
      Span: (0, 5)
  1. text1 ← world, match1 ← None, match2 ← <re.Match object; span=(0, 5), match='world'>

    6# Literal match7text1→ world = "world"8match1→ None = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"hello", text1world)9print(f"'hello' matches 'hello': {match1None is not None}")1011match2→ <re.Match object; span=(0, 5), match='world'> = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"world", text1world)12print(f"'hello' matches 'world': {match2<re.Match object; span=(0, 5), match='world'> is not None}")1314# Full match vs partial match15print("\nFull match vs search:")16print(f"Full match 'hello' in 'hello world': {re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r'hello', 'hello world') is not None}")17print(f"Search 'hello' in 'hello world': {re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r'hello', 'hello world') is not None}")1819# Compiled pattern20pattern→ re.compile('hello') = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.compile(r"hello")21match3→ <re.Match object; span=(0, 5), match='hello'> = patternre.compile('hello').search("hello world")22print(f"\nCompiled pattern found: {match3<re.Match object; span=(0, 5), match='hello'> is not None}")2324# Case sensitivity25match4→ None = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"hello", "Hello")26print(f"\n'Hello' matches 'hello': {match4None is not None}")2728# Case insensitive29match5→ <re.Match object; span=(0, 5), match='Hello'> = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"hello", "Hello", re.IGNORECASEre.IGNORECASE)30print(f"Case insensitive match: {match5<re.Match object; span=(0, 5), match='Hello'> is not None}")3132# Multiple occurrences33text2→ hello hello hello = "hello hello hello"34matches→ ⟨callable_iterator A⟩ = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.finditer(r"hello", text2hello hello hello)3536print("\nFind all occurrences:")37for match in matches:
    output'hello' matches 'hello': False
    'hello' matches 'world': True
    
    Full match vs search:
    Full match 'hello' in 'hello world': False
    Search 'hello' in 'hello world': True
    
    Compiled pattern found: True
    
    'Hello' matches 'hello': False
    Case insensitive match: True
    
    Find all occurrences:
  2. for match in matches:

    pass 1 of 3
    36print("\nFind all occurrences:")37for match<re.Match object; span=(0, 5), match='hello'> in matches⟨callable_iterator A⟩:38    print(f"  Found at index: {match<re.Match object; span=(0, 5), match='hello'>.start()}")
    output  Found at index: 0
    All 3 passes — pass 1 is the card above
    passmatch
    1<re.Match object; span=(0, 5), match='hello'>
    2<re.Match object; span=(6, 11), match='hello'>
    3<re.Match object; span=(12, 17), match='hello'>
  3. text3 ← hello world, match6 ← <re.Match object; span=(0, 5), match='hello'>

    40# Match object details41text3→ hello world = "hello world"42match6→ <re.Match object; span=(0, 5), match='hello'> = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"hello", text3hello world)43if match6:
  4. if match6:

    42match6 = re.search(r"hello", text3)43if match6<re.Match object; span=(0, 5), match='hello'>:44    print(f"\nMatch details:")45    print(f"  Matched text: {match6<re.Match object; span=(0, 5), match='hello'>.group()}")46    print(f"  Start: {match6<re.Match object; span=(0, 5), match='hello'>.start()}, End: {match6.end()}")47    print(f"  Span: {match6<re.Match object; span=(0, 5), match='hello'>.span()}")
    output
    Match details:
      Matched text: hello
      Start: 0, End: 5
      Span: (0, 5)
literal_match Basic exact text matching with re.match(), re.search(), and re.fullmatch()

Character Classes

character_class.py
Replay: real traced execution (multi-file project)
# Character classes

import re

# Character class [abc]
# Match one of specific characters
print("a" == re.fullmatch(r"[abc]", "a").group() if re.fullmatch(r"[abc]", "a") else False)
print("b" == re.fullmatch(r"[abc]", "b").group() if re.fullmatch(r"[abc]", "b") else False)
print("d matches [abc]:", re.fullmatch(r"[abc]", "d") is not None)

# Range
print("\nRanges:")
print("5 matches [0-9]:", re.fullmatch(r"[0-9]", "5") is not None)
print("m matches [a-z]:", re.fullmatch(r"[a-z]", "m") is not None)
print("M matches [A-Z]:", re.fullmatch(r"[A-Z]", "M") is not None)
print("M matches [a-z]:", re.fullmatch(r"[a-z]", "M") is not None)

# Multiple ranges
print("\nMultiple ranges:")
print("a matches [a-zA-Z]:", re.fullmatch(r"[a-zA-Z]", "a") is not None)
print("5 matches [a-zA-Z]:", re.fullmatch(r"[a-zA-Z]", "5") is not None)
print("5 matches [a-zA-Z0-9]:", re.fullmatch(r"[a-zA-Z0-9]", "5") is not None)

# Negation [^...]
print("\nNegation:")
print("a matches [^0-9]:", re.fullmatch(r"[^0-9]", "a") is not None)
print("5 matches [^0-9]:", re.fullmatch(r"[^0-9]", "5") is not None)

# Predefined character classes
print("\nPredefined classes:")
print(r"5 matches \d:", re.fullmatch(r"\d", "5") is not None)
print(r"a matches \d:", re.fullmatch(r"\d", "a") is not None)
print(r"a matches \w:", re.fullmatch(r"\w", "a") is not None)
print(r"space matches \s:", re.fullmatch(r"\s", " ") is not None)
print(r"a matches \D:", re.fullmatch(r"\D", "a") is not None)
print(r"5 matches \D:", re.fullmatch(r"\D", "5") is not None)

# Dot . matches any character
print("\nDot (any char):")
print("a matches .:", re.fullmatch(r".", "a") is not None)
print("5 matches .:", re.fullmatch(r".", "5") is not None)
print("space matches .:", re.fullmatch(r".", " ") is not None)

# Escape special chars
print("\nEscape special chars:")
print(r"\.:", re.search(r"\.", "hello.world") is not None)
print(r"literal dot at:", re.search(r"\.", "hello.world").start())

  1. print("a" == re.fullmatch(r"[abc]", "a").group() if re.fullmatch(r"[ab…

    6# Match one of specific characters7print("a" == re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[abc]", "a").group() if re.fullmatch(r"[abc]", "a") else False)8print("b" == re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[abc]", "b").group() if re.fullmatch(r"[abc]", "b") else False)9print("d matches [abc]:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[abc]", "d") is not None)1011# Range12print("\nRanges:")13print("5 matches [0-9]:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[0-9]", "5") is not None)14print("m matches [a-z]:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[a-z]", "m") is not None)15print("M matches [A-Z]:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[A-Z]", "M") is not None)16print("M matches [a-z]:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[a-z]", "M") is not None)1718# Multiple ranges19print("\nMultiple ranges:")20print("a matches [a-zA-Z]:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[a-zA-Z]", "a") is not None)21print("5 matches [a-zA-Z]:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[a-zA-Z]", "5") is not None)22print("5 matches [a-zA-Z0-9]:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[a-zA-Z0-9]", "5") is not None)2324# Negation [^...]25print("\nNegation:")26print("a matches [^0-9]:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[^0-9]", "a") is not None)27print("5 matches [^0-9]:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"[^0-9]", "5") is not None)2829# Predefined character classes30print("\nPredefined classes:")31print(r"5 matches \d:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"\d", "5") is not None)32print(r"a matches \d:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"\d", "a") is not None)33print(r"a matches \w:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"\w", "a") is not None)34print(r"space matches \s:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"\s", " ") is not None)35print(r"a matches \D:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"\D", "a") is not None)36print(r"5 matches \D:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"\D", "5") is not None)3738# Dot . matches any character39print("\nDot (any char):")40print("a matches .:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r".", "a") is not None)41print("5 matches .:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r".", "5") is not None)42print("space matches .:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r".", " ") is not None)4344# Escape special chars45print("\nEscape special chars:")46print(r"\.:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"\.", "hello.world") is not None)47print(r"literal dot at:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"\.", "hello.world").start())
    outputd matches [abc]: False
    
    Ranges:
    5 matches [0-9]: True
    m matches [a-z]: True
    M matches [A-Z]: True
    M matches [a-z]: False
    
    Multiple ranges:
    a matches [a-zA-Z]: True
    5 matches [a-zA-Z]: False
    5 matches [a-zA-Z0-9]: True
    
    Negation:
    a matches [^0-9]: True
    5 matches [^0-9]: False
    
    Predefined classes:
    5 matches \d: True
    a matches \d: False
    a matches \w: True
    space matches \s: True
    a matches \D: True
    5 matches \D: False
    
    Dot (any char):
    a matches .: True
    5 matches .: True
    space matches .: True
    
    Escape special chars:
    \.: True
    literal dot at: 5
character_class Matching sets of characters with [abc], [a-z], and predefined classes like \\d and \\w

Quantifiers

quantifiers.py
Replay: real traced execution (multi-file project)
# Quantifiers

import re

# Quantifiers
# * (0 or more)
print("Asterisk * (0 or more):")
print("'' matches a*:", re.fullmatch(r"a*", "") is not None)
print("'a' matches a*:", re.fullmatch(r"a*", "a") is not None)
print("'aaa' matches a*:", re.fullmatch(r"a*", "aaa") is not None)
print("'b' matches a*:", re.fullmatch(r"a*", "b") is not None)

# + (1 or more)
print("\nPlus + (1 or more):")
print("'' matches a+:", re.fullmatch(r"a+", "") is not None)
print("'a' matches a+:", re.fullmatch(r"a+", "a") is not None)
print("'aaa' matches a+:", re.fullmatch(r"a+", "aaa") is not None)

# ? (0 or 1)
print("\nQuestion ? (0 or 1):")
print("'' matches a?:", re.fullmatch(r"a?", "") is not None)
print("'a' matches a?:", re.fullmatch(r"a?", "a") is not None)
print("'aa' matches a?:", re.fullmatch(r"a?", "aa") is not None)

# {n} (exactly n)
print("\n{n} (exactly n):")
print("'aa' matches a{2}:", re.fullmatch(r"a{2}", "aa") is not None)
print("'aaa' matches a{2}:", re.fullmatch(r"a{2}", "aaa") is not None)
print("'a' matches a{2}:", re.fullmatch(r"a{2}", "a") is not None)

# {n,} (n or more)
print("\n{n,} (n or more):")
print("'aa' matches a{2,}:", re.fullmatch(r"a{2,}", "aa") is not None)
print("'aaa' matches a{2,}:", re.fullmatch(r"a{2,}", "aaa") is not None)
print("'a' matches a{2,}:", re.fullmatch(r"a{2,}", "a") is not None)

# {n,m} (between n and m)
print("\n{n,m} (between n and m):")
print("'aa' matches a{2,4}:", re.fullmatch(r"a{2,4}", "aa") is not None)
print("'aaa' matches a{2,4}:", re.fullmatch(r"a{2,4}", "aaa") is not None)
print("'aaaa' matches a{2,4}:", re.fullmatch(r"a{2,4}", "aaaa") is not None)
print("'a' matches a{2,4}:", re.fullmatch(r"a{2,4}", "a") is not None)
print("'aaaaa' matches a{2,4}:", re.fullmatch(r"a{2,4}", "aaaaa") is not None)

# Practical: digits
print("\nPractical - validate numbers:")
print(r"'123' matches \d+:", re.fullmatch(r"\d+", "123") is not None)
print(r"'12345' matches \d{5}:", re.fullmatch(r"\d{5}", "12345") is not None)
print(r"'123' matches \d{2,4}:", re.fullmatch(r"\d{2,4}", "123") is not None)

# Greedy vs non-greedy
print("\nGreedy vs non-greedy:")
text = "<tag>content</tag>"
greedy = re.search(r"<.*>", text)
non_greedy = re.search(r"<.*?>", text)
print(f"Greedy: {greedy.group()}")
print(f"Non-greedy: {non_greedy.group()}")

  1. text ← <tag>content</tag>, greedy ← <re.Match object; span=(0, 18), match='<tag>content</tag>'>

    6# * (0 or more)7print("Asterisk * (0 or more):")8print("'' matches a*:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a*", "") is not None)9print("'a' matches a*:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a*", "a") is not None)10print("'aaa' matches a*:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a*", "aaa") is not None)11print("'b' matches a*:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a*", "b") is not None)1213# + (1 or more)14print("\nPlus + (1 or more):")15print("'' matches a+:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a+", "") is not None)16print("'a' matches a+:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a+", "a") is not None)17print("'aaa' matches a+:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a+", "aaa") is not None)1819# ? (0 or 1)20print("\nQuestion ? (0 or 1):")21print("'' matches a?:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a?", "") is not None)22print("'a' matches a?:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a?", "a") is not None)23print("'aa' matches a?:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a?", "aa") is not None)2425# {n} (exactly n)26print("\n{n} (exactly n):")27print("'aa' matches a{2}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a{2}", "aa") is not None)28print("'aaa' matches a{2}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a{2}", "aaa") is not None)29print("'a' matches a{2}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a{2}", "a") is not None)3031# {n,} (n or more)32print("\n{n,} (n or more):")33print("'aa' matches a{2,}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a{2,}", "aa") is not None)34print("'aaa' matches a{2,}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a{2,}", "aaa") is not None)35print("'a' matches a{2,}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a{2,}", "a") is not None)3637# {n,m} (between n and m)38print("\n{n,m} (between n and m):")39print("'aa' matches a{2,4}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a{2,4}", "aa") is not None)40print("'aaa' matches a{2,4}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a{2,4}", "aaa") is not None)41print("'aaaa' matches a{2,4}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a{2,4}", "aaaa") is not None)42print("'a' matches a{2,4}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a{2,4}", "a") is not None)43print("'aaaaa' matches a{2,4}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"a{2,4}", "aaaaa") is not None)4445# Practical: digits46print("\nPractical - validate numbers:")47print(r"'123' matches \d+:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"\d+", "123") is not None)48print(r"'12345' matches \d{5}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"\d{5}", "12345") is not None)49print(r"'123' matches \d{2,4}:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"\d{2,4}", "123") is not None)5051# Greedy vs non-greedy52print("\nGreedy vs non-greedy:")53text→ <tag>content</tag> = "<tag>content</tag>"54greedy→ <re.Match object; span=(0, 18), match='<tag>content</tag>'> = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"<.*>", text<tag>content</tag>)55non_greedy→ <re.Match object; span=(0, 5), match='<tag>'> = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"<.*?>", text<tag>content</tag>)56print(f"Greedy: {greedy<re.Match object; span=(0, 18), match='<tag>content</tag>'>.group()}")57print(f"Non-greedy: {non_greedy<re.Match object; span=(0, 5), match='<tag>'>.group()}")
    outputAsterisk * (0 or more):
    '' matches a*: True
    'a' matches a*: True
    'aaa' matches a*: True
    'b' matches a*: False
    
    Plus + (1 or more):
    '' matches a+: False
    'a' matches a+: True
    'aaa' matches a+: True
    
    Question ? (0 or 1):
    '' matches a?: True
    'a' matches a?: True
    'aa' matches a?: False
    
    {n} (exactly n):
    'aa' matches a{2}: True
    'aaa' matches a{2}: False
    'a' matches a{2}: False
    
    {n,} (n or more):
    'aa' matches a{2,}: True
    'aaa' matches a{2,}: True
    'a' matches a{2,}: False
    
    {n,m} (between n and m):
    'aa' matches a{2,4}: True
    'aaa' matches a{2,4}: True
    'aaaa' matches a{2,4}: True
    'a' matches a{2,4}: False
    'aaaaa' matches a{2,4}: False
    
    Practical - validate numbers:
    '123' matches \d+: True
    '12345' matches \d{5}: True
    '123' matches \d{2,4}: True
    
    Greedy vs non-greedy:
    Greedy: <tag>content</tag>
    Non-greedy: <tag>
quantifiers Specifying repetition with *, +, ?, and {n,m}

Anchors

anchors.py
Replay: real traced execution (multi-file project)
# Anchors

import re

# Anchors ^ and $
# ^ (start of string)
print("Start anchor ^:")
print("'hello' matches ^hello:", re.match(r"^hello", "hello") is not None)
print("'hello world' matches ^hello:", re.match(r"^hello", "hello world") is not None)
print("'world hello' matches ^hello:", re.match(r"^hello", "world hello") is not None)

# $ (end of string)
print("\nEnd anchor $:")
print("'hello' matches hello$:", re.search(r"hello$", "hello") is not None)
print("'hello world' matches world$:", re.search(r"world$", "hello world") is not None)
print("'world hello' matches world$:", re.search(r"world$", "world hello") is not None)

# Both anchors
print("\nBoth anchors ^...$:")
print("'hello' matches ^hello$:", re.fullmatch(r"^hello$", "hello") is not None)
print("'hello world' matches ^hello$:", re.fullmatch(r"^hello$", "hello world") is not None)

# \b (word boundary)
print(r"\nWord boundary \b:")
text = "hello world"
print(f"Text: '{text}'")
print(r"\bhello\b found:", re.search(r"\bhello\b", text) is not None)
print(r"\bworld\b found:", re.search(r"\bworld\b", text) is not None)
print(r"\bhello\b in 'helloworld':", re.search(r"\bhello\b", "helloworld") is not None)

# Practical examples
print("\nPractical validation:")

# Must start with letter
print("'abc123' starts with letter:", re.match(r"^[a-zA-Z]", "abc123") is not None)
print("'123abc' starts with letter:", re.match(r"^[a-zA-Z]", "123abc") is not None)

# Must end with digit
print(r"'abc123' ends with digit:", re.search(r"\d$", "abc123") is not None)
print(r"'abc' ends with digit:", re.search(r"\d$", "abc") is not None)

# Exact length
print(r"'12345' is exactly 5 digits:", re.fullmatch(r"^\d{5}$", "12345") is not None)
print(r"'1234' is exactly 5 digits:", re.fullmatch(r"^\d{5}$", "1234") is not None)

# Multiple words
text2 = "The quick brown fox"
words = re.findall(r"\b\w+\b", text2)
print(f"\nWords in '{text2}':")
for word in words:
    print(f"  {word}")

# Line anchors with multiline
text3 = """line1
line2
line3"""
print("\nMultiline mode:")
matches = re.findall(r"^line", text3, re.MULTILINE)
print(f"Lines starting with 'line': {matches}")

  1. text ← hello world, text2 ← The quick brown fox, words ← ['The', 'quick', 'brown', 'fox']

    6# ^ (start of string)7print("Start anchor ^:")8print("'hello' matches ^hello:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"^hello", "hello") is not None)9print("'hello world' matches ^hello:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"^hello", "hello world") is not None)10print("'world hello' matches ^hello:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"^hello", "world hello") is not None)1112# $ (end of string)13print("\nEnd anchor $:")14print("'hello' matches hello$:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"hello$", "hello") is not None)15print("'hello world' matches world$:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"world$", "hello world") is not None)16print("'world hello' matches world$:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"world$", "world hello") is not None)1718# Both anchors19print("\nBoth anchors ^...$:")20print("'hello' matches ^hello$:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"^hello$", "hello") is not None)21print("'hello world' matches ^hello$:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"^hello$", "hello world") is not None)2223# \b (word boundary)24print(r"\nWord boundary \b:")25text→ hello world = "hello world"26print(f"Text: '{texthello world}'")27print(r"\bhello\b found:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"\bhello\b", texthello world) is not None)28print(r"\bworld\b found:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"\bworld\b", texthello world) is not None)29print(r"\bhello\b in 'helloworld':", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"\bhello\b", "helloworld") is not None)3031# Practical examples32print("\nPractical validation:")3334# Must start with letter35print("'abc123' starts with letter:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"^[a-zA-Z]", "abc123") is not None)36print("'123abc' starts with letter:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.match(r"^[a-zA-Z]", "123abc") is not None)3738# Must end with digit39print(r"'abc123' ends with digit:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"\d$", "abc123") is not None)40print(r"'abc' ends with digit:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"\d$", "abc") is not None)4142# Exact length43print(r"'12345' is exactly 5 digits:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"^\d{5}$", "12345") is not None)44print(r"'1234' is exactly 5 digits:", re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.fullmatch(r"^\d{5}$", "1234") is not None)4546# Multiple words47text2→ The quick brown fox = "The quick brown fox"48words→ ['The', 'quick', 'brown', 'fox'] = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.findall(r"\b\w+\b", text2The quick brown fox)49print(f"\nWords in '{text2The quick brown fox}':")50for word in words:
    outputStart anchor ^:
    'hello' matches ^hello: True
    'hello world' matches ^hello: True
    'world hello' matches ^hello: False
    
    End anchor $:
    'hello' matches hello$: True
    'hello world' matches world$: True
    'world hello' matches world$: False
    
    Both anchors ^...$:
    'hello' matches ^hello$: True
    'hello world' matches ^hello$: False
    \nWord boundary \b:
    Text: 'hello world'
    \bhello\b found: True
    \bworld\b found: True
    \bhello\b in 'helloworld': False
    
    Practical validation:
    'abc123' starts with letter: True
    '123abc' starts with letter: False
    'abc123' ends with digit: True
    'abc' ends with digit: False
    '12345' is exactly 5 digits: True
    '1234' is exactly 5 digits: False
    
    Words in 'The quick brown fox':
  2. for word in words:

    pass 1 of 4
    49print(f"\nWords in '{text2}':")50for wordThe in words['The', 'quick', 'brown', 'fox']:51    print(f"  {wordThe}")
    output  The
    All 4 passes — pass 1 is the card above
    password
    1The
    2quick
    3brown
    4fox
  3. text3 ← line1 line2 line3, matches ← ['line', 'line', 'line']

    53# Line anchors with multiline54text3→ line1
    line2
    line3 = """line155line256line3"""57print("\nMultiline mode:")58matches→ ['line', 'line', 'line'] = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.findall(r"^line", text3line1
    line2
    line3, re.MULTILINEre.MULTILINE)59print(f"Lines starting with 'line': {matches['line', 'line', 'line']}")
    output
    Multiline mode:
    Lines starting with 'line': ['line', 'line', 'line']
anchors Matching positions with ^ (start), $ (end), and \\b (word boundary)

Groups

groups.py
Replay: real traced execution (multi-file project)
# Groups and capturing

import re

# Groups with ()
# Basic grouping
date = "2025-01-29"
pattern = re.compile(r"(\d{4})-(\d{2})-(\d{2})")
match = pattern.match(date)

if match:
    print(f"Full match: {match.group(0)}")
    print(f"Year: {match.group(1)}")
    print(f"Month: {match.group(2)}")
    print(f"Day: {match.group(3)}")

# Email parsing
email = "user@example.com"
email_pattern = re.compile(r"([^@]+)@([^@]+)")
email_match = email_pattern.match(email)

if email_match:
    print("\nEmail parts:")
    print(f"Username: {email_match.group(1)}")
    print(f"Domain: {email_match.group(2)}")

# Phone number
phone = "(555) 123-4567"
phone_pattern = re.compile(r"\((\d{3})\)\s(\d{3})-(\d{4})")
phone_match = phone_pattern.match(phone)

if phone_match:
    print("\nPhone parts:")
    print(f"Area: {phone_match.group(1)}")
    print(f"Exchange: {phone_match.group(2)}")
    print(f"Number: {phone_match.group(3)}")

# Multiple matches
text = "Call me at 555-1234 or 555-5678"
num_pattern = re.compile(r"(\d{3})-(\d{4})")
num_matches = num_pattern.finditer(text)

print("\nAll phone numbers:")
for match in num_matches:
    print(f"  {match.group(0)} (Area: {match.group(1)}, Num: {match.group(2)})")

# Named groups
url = "https://example.com"
url_pattern = re.compile(r"(?P<protocol>https?)://(?P<domain>.+)")
url_match = url_pattern.match(url)

if url_match:
    print("\nURL parts (named groups):")
    print(f"Protocol: {url_match.group('protocol')}")
    print(f"Domain: {url_match.group('domain')}")

# Non-capturing group (?:...)
text2 = "color: red; colour: blue"
# Match both spellings but don't capture the 'u'
pattern2 = re.compile(r"colou?r:\s(\w+)")
matches2 = pattern2.findall(text2)
print(f"\nColors: {matches2}")

# Groups with findall
text3 = "2025-01-29 and 2024-12-25"
dates = re.findall(r"(\d{4})-(\d{2})-(\d{2})", text3)
print("\nAll dates (as tuples):")
for year, month, day in dates:
    print(f"  {year}/{month}/{day}")

# Backreferences
text4 = "hello hello"
# \1 refers to first group
duplicate = re.search(r"(\w+)\s\1", text4)
if duplicate:
    print(f"\nDuplicate word found: {duplicate.group(1)}")

  1. date ← 2025-01-29, pattern ← re.compile('(\\d{4})-(\\d{2})-(\\d{2})')

    6# Basic grouping7date→ 2025-01-29 = "2025-01-29"8pattern→ re.compile('(\\d{4})-(\\d{2})-(\\d{2})') = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.compile(r"(\d{4})-(\d{2})-(\d{2})")9match→ <re.Match object; span=(0, 10), match='2025-01-29'> = patternre.compile('(\\d{4})-(\\d{2})-(\\d{2})').match(date2025-01-29)
  2. if match:

    11if match<re.Match object; span=(0, 10), match='2025-01-29'>:12    print(f"Full match: {match<re.Match object; span=(0, 10), match='2025-01-29'>.group(0)}")13    print(f"Year: {match<re.Match object; span=(0, 10), match='2025-01-29'>.group(1)}")14    print(f"Month: {match<re.Match object; span=(0, 10), match='2025-01-29'>.group(2)}")15    print(f"Day: {match<re.Match object; span=(0, 10), match='2025-01-29'>.group(3)}")
    outputFull match: 2025-01-29
    Year: 2025
    Month: 01
    Day: 29
  3. email ← user@example.com, email_pattern ← re.compile('([^@]+)@([^@]+)')

    17# Email parsing18email→ user@example.com = "user@example.com"19email_pattern→ re.compile('([^@]+)@([^@]+)') = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.compile(r"([^@]+)@([^@]+)")20email_match→ <re.Match object; span=(0, 16), match='user@example.com'> = email_patternre.compile('([^@]+)@([^@]+)').match(emailuser@example.com)
  4. if email_match:

    22if email_match<re.Match object; span=(0, 16), match='user@example.com'>:23    print("\nEmail parts:")24    print(f"Username: {email_match<re.Match object; span=(0, 16), match='user@example.com'>.group(1)}")25    print(f"Domain: {email_match<re.Match object; span=(0, 16), match='user@example.com'>.group(2)}")
    output
    Email parts:
    Username: user
    Domain: example.com
  5. phone ← (555) 123-4567, phone_pattern ← re.compile('\\((\\d{3})\\)\\s(\\d{3})-(\\d{4})')

    27# Phone number28phone→ (555) 123-4567 = "(555) 123-4567"29phone_pattern→ re.compile('\\((\\d{3})\\)\\s(\\d{3})-(\\d{4})') = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.compile(r"\((\d{3})\)\s(\d{3})-(\d{4})")30phone_match→ <re.Match object; span=(0, 14), match='(555) 123-4567'> = phone_patternre.compile('\\((\\d{3})\\)\\s(\\d{3})-(\\d{4})').match(phone(555) 123-4567)
  6. if phone_match:

    32if phone_match<re.Match object; span=(0, 14), match='(555) 123-4567'>:33    print("\nPhone parts:")34    print(f"Area: {phone_match<re.Match object; span=(0, 14), match='(555) 123-4567'>.group(1)}")35    print(f"Exchange: {phone_match<re.Match object; span=(0, 14), match='(555) 123-4567'>.group(2)}")36    print(f"Number: {phone_match<re.Match object; span=(0, 14), match='(555) 123-4567'>.group(3)}")
    output
    Phone parts:
    Area: 555
    Exchange: 123
    Number: 4567
  7. text ← Call me at 555-1234 or 555-5678, num_pattern ← re.compile('(\\d{3})-(\\d{4})')

    38# Multiple matches39text→ Call me at 555-1234 or 555-5678 = "Call me at 555-1234 or 555-5678"40num_pattern→ re.compile('(\\d{3})-(\\d{4})') = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.compile(r"(\d{3})-(\d{4})")41num_matches→ ⟨callable_iterator A⟩ = num_patternre.compile('(\\d{3})-(\\d{4})').finditer(textCall me at 555-1234 or 555-5678)4243print("\nAll phone numbers:")44for match in num_matches:
    output
    All phone numbers:
  8. for match in num_matches:

    pass 1 of 2
    43print("\nAll phone numbers:")44for match<re.Match object; span=(11, 19), match='555-1234'> in num_matches⟨callable_iterator A⟩:45    print(f"  {match<re.Match object; span=(11, 19), match='555-1234'>.group(0)} (Area: {match.group(1)}, Num: {match.group(2)})")
    output  555-1234 (Area: 555, Num: 1234)
  9. for match in num_matches:

    pass 2 of 2
    43print("\nAll phone numbers:")44for match<re.Match object; span=(23, 31), match='555-5678'> in num_matches⟨callable_iterator A⟩:45    print(f"  {match<re.Match object; span=(23, 31), match='555-5678'>.group(0)} (Area: {match.group(1)}, Num: {match.group(2)})")
    output  555-5678 (Area: 555, Num: 5678)
  10. url ← https://example.com, url_pattern ← re.compile('(?P<protocol>https?)://(?P<domain>.+)')

    47# Named groups48url→ https://example.com = "https://example.com"49url_pattern→ re.compile('(?P<protocol>https?)://(?P<domain>.+)') = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.compile(r"(?P<protocol>https?)://(?P<domain>.+)")50url_match→ <re.Match object; span=(0, 19), match='https://example.com'> = url_patternre.compile('(?P<protocol>https?)://(?P<domain>.+)').match(urlhttps://example.com)
  11. if url_match:

    52if url_match<re.Match object; span=(0, 19), match='https://example.com'>:53    print("\nURL parts (named groups):")54    print(f"Protocol: {url_match<re.Match object; span=(0, 19), match='https://example.com'>.group('protocol')}")55    print(f"Domain: {url_match<re.Match object; span=(0, 19), match='https://example.com'>.group('domain')}")
    output
    URL parts (named groups):
    Protocol: https
    Domain: example.com
  12. text2 ← color: red; colour: blue, pattern2 ← re.compile('colou?r:\\s(\\w+)')

    57# Non-capturing group (?:...)58text2→ color: red; colour: blue = "color: red; colour: blue"59# Match both spellings but don't capture the 'u'60pattern2→ re.compile('colou?r:\\s(\\w+)') = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.compile(r"colou?r:\s(\w+)")61matches2→ ['red', 'blue'] = pattern2re.compile('colou?r:\\s(\\w+)').findall(text2color: red; colour: blue)62print(f"\nColors: {matches2['red', 'blue']}")6364# Groups with findall65text3→ 2025-01-29 and 2024-12-25 = "2025-01-29 and 2024-12-25"66dates→ [('2025', '01', '29'), ('2024', '12', '25')] = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.findall(r"(\d{4})-(\d{2})-(\d{2})", text32025-01-29 and 2024-12-25)67print("\nAll dates (as tuples):")68for year, month, day in dates:
    output
    Colors: ['red', 'blue']
    
    All dates (as tuples):
  13. for year, month, day in dates:

    pass 1 of 2
    67print("\nAll dates (as tuples):")68for year2025, month01, day29 in dates[('2025', '01', '29'), ('2024', '12', '25')]:69    print(f"  {year2025}/{month01}/{day29}")
    output  2025/01/29
  14. for year, month, day in dates:

    pass 2 of 2
    67print("\nAll dates (as tuples):")68for year2024, month12, day25 in dates[('2025', '01', '29'), ('2024', '12', '25')]:69    print(f"  {year2024}/{month12}/{day25}")
    output  2024/12/25
  15. text4 ← hello hello, duplicate ← <re.Match object; span=(0, 11), match='hello hello'>

    71# Backreferences72text4→ hello hello = "hello hello"73# \1 refers to first group74duplicate→ <re.Match object; span=(0, 11), match='hello hello'> = re<module 're' from '/usr/local/lib/python3.12/re/__init__.py'>.search(r"(\w+)\s\1", text4hello hello)75if duplicate:
  16. if duplicate:

    74duplicate = re.search(r"(\w+)\s\1", text4)75if duplicate<re.Match object; span=(0, 11), match='hello hello'>:76    print(f"\nDuplicate word found: {duplicate<re.Match object; span=(0, 11), match='hello hello'>.group(1)}")
    output
    Duplicate word found: hello
groups Capturing and extracting parts of matches with parentheses

Common Functions

  • re.match(): Match at start
  • re.search(): Find pattern anywhere
  • re.findall(): Find all matches
  • re.sub(): Replace matches

Exercise: practical.py

Build validators for usernames, emails, phones, and URLs using regex