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 0Remember
* + ? {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.