Quantifiers and alternation

re · Essentials · 9 min

Real data varies in length, and quantifiers are how patterns flex to fit it.

A quantifier applies to the item just before it (a character, class or group):

  • * zero or more
  • + one or more
  • ? zero or one (optional)
  • {3} exactly three, {2,4} two to four, {2,} two or more

So r"\d{3}-\d{4}" matches 555-1234, and r"colou?r" matches both spellings.

To repeat more than one character, group it: r"(ha)+" matches hahaha, while r"ha+" matches haaa.

Alternation | means "this or that" and has the lowest precedence: r"cat|dog" matches either word. r"^cat|dog$" means ^cat or dog$, not both anchored; write r"^(cat|dog)$" to anchor the whole choice. Alternatives are tried left to right and the first one that leads to an overall match wins, so put longer alternatives first when one is a prefix of another (r"https|http").

Quantifiers are greedy by default: they take as much as possible and give back only if the rest of the pattern fails. That matters when patterns contain .*, covered in the greedy versus lazy lesson.

Be careful with *: a pattern like r"\d*" happily matches the empty string, which can give surprising results with search or findall.

Example

import re

print(re.findall(r"\d{3}-\d{4}", "call 555-1234 or 555-98765"))  # ['555-1234', '555-9876']
print(re.findall(r"colou?r", "color colour colouur"))        # ['color', 'colour']
print(re.fullmatch(r"(ha)+", "hahaha") is not None)          # True
print(re.fullmatch(r"ha+", "hahaha") is not None)            # False
print(re.findall(r"\b\w{5,}\b", "a quick brown fox jumps"))  # ['quick', 'brown', 'jumps']

print(re.search(r"http|https", "https://x").group())         # http
print(re.search(r"https?", "https://x").group())             # https
print(re.search(r"\d*", "abc123").group() == "")            # True: empty match at 0

Remember

  • * + ? {m,n} repeat the preceding item; group to repeat several characters
  • | has the lowest precedence; group alternatives before anchoring
  • * and ? allow empty matches, which can surprise you

Watch out for

  • Writing ^a|b$ and expecting both alternatives to be anchored at both ends.

Check yourself

  1. What does re.findall(r'ab+', 'ab abbb a b') return?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/re.html#regular-expression-syntax