🎓 A1Learner
🔍

Python Regex Patterns in Depth

A quantifier says how many times a part may repeat. A group keeps part of a match for reuse.

Repeat with * + and ?

Each quantifier changes how much must appear.

import re
print(re.findall(r"ab*", "a ab abb"))
print(re.findall(r"ab+", "a ab abb"))
print(re.findall(r"colou?r", "color colour"))

Repeat a set number of times

Curly braces set a low and high limit.

import re
print(re.findall(r"\d{2,3}", "1 12 123 1234"))

Greedy and lazy

Greedy takes the longest match. Lazy takes the shortest match.

import re
print(re.search(r"<.*>", "<b>bold</b>").group())
print(re.search(r"<.*?>", "<b>bold</b>").group())

Named groups

A named group lets you read a part by name.

import re
match = re.search(r"(?P<area>\d{3})-(?P<num>\d{4})", "call 555-1234")
print(match.group("area"), match.group("num"))

Choose with alternation

The | sign means or inside a pattern.

import re
print(re.findall(r"cat|dog", "I have a cat and a dog"))

Replace with a function

re.sub can call a function for each match. The function returns the replacement text.

import re
result = re.sub(r"\d+", lambda m: str(int(m.group()) * 2), "3 cats and 5 dogs")
print(result)

Split with a pattern

re.split cuts a string wherever the pattern matches.

import re
print(re.split(r"[,;]", "a,b;c,d"))

Example

import re

lines = ["Ann: 555-123-4567", "Bo: (555) 987 6543", "Cy: 555.111.2222"]

def normalise(match):
    digits = re.sub(r"\D", "", match.group())
    return f"{digits[0:3]}-{digits[3:6]}-{digits[6:10]}"

for line in lines:
    print(re.sub(r"\(?\d[\d .()-]*\d", normalise, line))

Key points

  • Quantifier: a mark that says how many repeats are allowed
  • Use * for zero or more, + for one or more, and ? for zero or one
  • Use {m,n} for at least m and at most n repeats
  • Use (?P<name>...) to give a group a name
  • Use | inside a pattern to offer a choice

In this example, each contact line holds one phone number. The outer sub finds the number part in each line. It calls normalise for that match. normalise removes every non-digit with a second sub. It then builds one clean format from the ten digits. Every line prints in the same format.

Note

Greedy matching takes as much text as it can.

Lazy matching with ? takes as little as it can.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Python Playground

Exercise

What does a lazy quantifier like .*? do?