Nearby lessons

130 of 159

Python - Regex Quantifiers

📌 What You Will Learn
  • Understand what Quantifiers are in Regular Expressions
  • Know the meaning of the quantifiers a, a+, a*, and a?
  • Match exact and ranged repetitions with a{m} and a{m,n}
  • Explain why a* and a? can produce empty matches
  • Recognize that quantifiers are greedy by default

Introduction to Quantifiers

In Regular Expressions, Quantifiers are used to specify the number of times a character or pattern should occur.

Consider the character:

a
        

Using Quantifiers, we can ask questions such as:

  • Is exactly one a present?
  • Are one or more a characters present?
  • Are zero or more a characters present?
  • Is zero or one a present?
  • Are exactly a specified number of a characters present?
  • Are between a minimum and maximum number of a characters present?

Simple Definition:

Quantifiers specify how many times a particular character or pattern should occur.

Quantifiers Covered

The following six Quantifiers are covered in this section:

Quantifier Meaning
a Exactly one a
a+ At least one a
a* Any number of a characters, including zero
a? At most one a, meaning zero or one
a{m} Exactly m number of a characters
a{m,n} Minimum m and maximum n number of a characters

Target String Used in the Demo

The document demonstrates Quantifiers using the target string:

abaabaaab
        

Index positions are:

Character : a b a a b a a a b
Index     : 0 1 2 3 4 5 6 7 8
        

Notice the groups of a:

a  b  aa  b  aaa  b
│     ││     │││
1      2       3
        

This makes the string useful for understanding how different Quantifiers behave.

Complete Demo Program

Keep the target string:

abaabaaab
        

and change the Regular Expression inside re.finditer():

a
a+
a*
a?
a{3}
a{2,3}
        

The same program can therefore be used to understand all the Quantifiers.

🐍Code Cell
1import re
2 
3matcher = re.finditer("a", "abaabaaab")
4 
5for match in matcher:
6 print(match.start(), "...", match.group())
Output
No output captured.

1. a - Exactly One a

The pattern:

a
        

searches for exactly one occurrence of a at a time.

It does not group consecutive a characters together.

The Regex engine searches from left to right:

a b a a b a a a b
0 1 2 3 4 5 6 7 8
↑   ↑ ↑   ↑ ↑ ↑
        

Every individual a is treated as a separate match. For abaabaaab, a occurs at indexes:

0, 2, 3, 5, 6, 7
        
Start Index group()
0a
2a
3a
5a
6a
7a
🐍Code Cell
1import re
2 
3matcher = re.finditer("a", "abaabaaab")
4 
5for match in matcher:
6 print(match.start(), "...", match.group())
Output
0 ... a
2 ... a
3 ... a
5 ... a
6 ... a
7 ... a

2. a+ - At Least One a

The Quantifier:

a+
        

means:

At least one a.

In other words:

1 or more a characters
        

It can match:

a
aa
aaa
aaaa
...
        

but it cannot match zero occurrences.

The + quantifier is greedy by default, so consecutive a characters are matched together as much as possible.

Break the target into groups:

a | b | aa | b | aaa | b
        

a+ requires one or more consecutive a characters. Therefore:

Index 0 → a
Index 2 → aa
Index 5 → aaa
        

Unlike the pattern a, consecutive a characters are grouped into one match.

🐍Code Cell
1import re
2 
3matcher = re.finditer("a+", "abaabaaab")
4 
5for match in matcher:
6 print(match.start(), "...", match.group())
Output
0 ... a
2 ... aa
5 ... aaa

3. a* - Zero or More a Characters

The Quantifier:

a*
        

means:

Any number of a characters, including zero.

It can match:

""       → zero a
a        → one a
aa       → two a characters
aaa      → three a characters
aaaa...  → more a characters
        

This is different from a+ because a* is also allowed to match when no a is present at the current position.

The target is:

a b a a b a a a b
0 1 2 3 4 5 6 7 8
        

At index 0, a matches, so the output is 0 ... a. At index 1, the character is b. Because * allows zero occurrences of a, an empty match is possible: 1 .... At index 2, the consecutive a characters are aa, so the output is 2 ... aa. At index 4, we again have b, so an empty match occurs. At index 5, aaa is matched together. At index 8, b produces another empty match. Finally, an empty match is also possible at index 9, which is the position immediately after the last character.

🐍Code Cell
1import re
2 
3matcher = re.finditer("a*", "abaabaaab")
4 
5for match in matcher:
6 print(match.start(), "...", match.group())
Output
0 ... a
1 ...
2 ... aa
4 ...
5 ... aaa
8 ...
9 ...

Why Does a* Produce Empty Matches?

The key point is:

* means zero or more
        

Therefore, the Regex engine does not require an a to be present.

For example, consider:

b
^
        

Before b, the pattern can say:

There are zero consecutive a characters here.
        

That is a valid match for:

a*
        

The matched text is an empty string:

""
        

Therefore group() prints nothing after the dots:

1 ...
        

This does not mean that b matched a*. It means an empty string of zero a characters matched at that position.

The string abaabaaab contains 9 characters with indexes:

0 to 8
        

But a Regular Expression can also match an empty string at the position immediately after the final character:

a b a a b a a a b |
0 1 2 3 4 5 6 7 8 9
                  ↑
            End position
        

Because a* accepts zero a characters, it can produce:

9 ...
        

4. a? - At Most One a

The Quantifier:

a?
        

means:

At most one a.

That means:

zero a
OR
one a
        

It cannot consume two or more a characters in a single match.

The question mark permits 0 or 1 occurrence. Therefore, every a is matched individually. At positions containing b, zero occurrences are valid, so empty matches occur:

Index 0 → a
Index 1 → empty
Index 2 → a
Index 3 → a
Index 4 → empty
Index 5 → a
Index 6 → a
Index 7 → a
Index 8 → empty
Index 9 → empty
        

Unlike a*, a? does not combine aa or aaa into one match because it allows at most one a per match.

🐍Code Cell
1import re
2 
3matcher = re.finditer("a?", "abaabaaab")
4 
5for match in matcher:
6 print(match.start(), "...", match.group())
Output
0 ... a
1 ...
2 ... a
3 ... a
4 ...
5 ... a
6 ... a
7 ... a
8 ...
9 ...

a* vs a?

Pattern Meaning Can Match Empty? Maximum Number in One Match
a* Zero or more Yes No fixed maximum
a? Zero or one Yes 1

For example, for:

aaa
        

a* can match:

aaa
        

whereas a? matches one a at a time.

5. a{m} - Exactly m Occurrences

The Quantifier:

a{m}
        

means:

Exactly m number of a characters.

For example:

a{3}
        

means:

Exactly three consecutive a characters
        

It matches:

aaa
        

The target contains groups:

a | aa | aaa
        

The pattern a{3} requires exactly three consecutive a characters for the match. The first group has only one:

a → Not enough
        

The second group has two:

aa → Not enough
        

The third group has three:

aaa → Match
        

It starts at index 5.

More examples of a{m}:

Pattern Meaning
a{1} Exactly one a
a{2} Exactly two consecutive a characters
a{3} Exactly three consecutive a characters
a{5} Exactly five consecutive a characters
🐍Code Cell
1import re
2 
3matcher = re.finditer("a{3}", "abaabaaab")
4 
5for match in matcher:
6 print(match.start(), "...", match.group())
Output
5 ... aaa

6. a{m,n} - Minimum m and Maximum n

The Quantifier:

a{m,n}
        

means:

Minimum m and maximum n number of consecutive a characters.

For example:

a{2,3}
        

means:

Minimum = 2
Maximum = 3
        

Therefore, it can match:

aa
aaa
        

By default, this quantifier is greedy, so when both lengths are possible at the same position, Python prefers the longer allowed match.

Our target contains:

a | aa | aaa
        

The first group a contains only one a, so it does not satisfy the minimum requirement of 2. The second group aa contains two a characters, so it matches. The third group aaa contains three a characters, so it also matches.

🐍Code Cell
1import re
2 
3matcher = re.finditer("a{2,3}", "abaabaaab")
4 
5for match in matcher:
6 print(match.start(), "...", match.group())
Output
2 ... aa
5 ... aaa

Every Quantifier Output

The complete output of every Quantifier on the target string abaabaaab:

Pattern: a

0 ... a
2 ... a
3 ... a
5 ... a
6 ... a
7 ... a
        

Pattern: a+

0 ... a
2 ... aa
5 ... aaa
        

Pattern: a*

0 ... a
1 ...
2 ... aa
4 ...
5 ... aaa
8 ...
9 ...
        

Pattern: a?

0 ... a
1 ...
2 ... a
3 ... a
4 ...
5 ... a
6 ... a
7 ... a
8 ...
9 ...
        

Pattern: a{3}

5 ... aaa
        

Pattern: a{2,3}

2 ... aa
5 ... aaa
        

Complete Quantifier Comparison

Pattern Meaning Example Matches Can Match Empty?
a Exactly one a No
a+ One or more a, aa, aaa... No
a* Zero or more empty, a, aa, aaa... Yes
a? Zero or one empty, a Yes
a{m} Exactly m For m=3: aaa Normally No
a{m,n} Between m and n For {2,3}: aa or aaa Only if minimum is 0

Important Concept - Greedy Behaviour

Quantifiers such as:

+
*
{m,n}
        

are greedy by default.

This means they try to consume as many matching characters as the pattern permits.

Example:

Target  = aaa
Pattern = a+
        

Python prefers:

aaa
        

rather than stopping after:

a
        

Similarly:

Pattern = a{2,3}
Target  = aaa
        

matches:

aaa
        

because three is within the allowed range and is the longest possible match.

Empty Matches - Complete Concept

Among the basic Quantifiers discussed here:

a*
a?
        

can match an empty string because their minimum required number of a characters is zero.

Pattern Minimum Required Empty Match?
a 1 No
a+ 1 No
a* 0 Yes
a? 0 Yes
a{3} 3 No
a{2,3} 2 No

The basic rule is:

Minimum occurrence = 0
        │
        ▼
Empty match is possible
        

Execution Flow

The complete execution flow of the Quantifier program:

Program Starts
      │
      ▼
import re
      │
      ▼
Select Pattern
      │
      ├── a
      ├── a+
      ├── a*
      ├── a?
      ├── a{3}
      └── a{2,3}
      │
      ▼
Target String
"abaabaaab"
      │
      ▼
re.finditer()
      │
      ▼
Start Searching
from Left to Right
      │
      ▼
Check Pattern at
Current Position
      │
   ┌──┴───────────┐
   │              │
Match Possible   No Match
   │              │
   ▼              ▼
Create Match    Move Forward
Object
   │
   ▼
Print start()
and group()
   │
   ▼
Continue Search
      │
      ▼
End of Target
      │
      ▼
Program Ends

Execution Flow - a+

Target = abaabaaab
Pattern = a+

Start
  │
  ▼
Index 0
  │
  ▼
"a" found
  │
  ▼
Match → a
  │
  ▼
Next search starts after match
  │
  ▼
Index 2
  │
  ▼
"aa" found
  │
  ▼
Match → aa
  │
  ▼
Continue
  │
  ▼
Index 5
  │
  ▼
"aaa" found
  │
  ▼
Match → aaa
  │
  ▼
No more matching groups
  │
  ▼
End

Execution Flow - a* with Empty Matches

Target = abaabaaab
Pattern = a*

Index 0
  │
  ▼
Match "a"
  │
  ▼
Index 1
  │
  ▼
No a present
but zero a is valid
  │
  ▼
Empty Match
  │
  ▼
Index 2
  │
  ▼
Match "aa"
  │
  ▼
Index 4
  │
  ▼
Empty Match
  │
  ▼
Index 5
  │
  ▼
Match "aaa"
  │
  ▼
Index 8
  │
  ▼
Empty Match
  │
  ▼
Index 9
  │
  ▼
End position
Zero a is valid
  │
  ▼
Empty Match
  │
  ▼
End

Important Points

  1. Do not confuse a with a+. The first matches one a at a time, while the second can combine consecutive a characters.
  2. + means one or more, * means zero or more, and ? means zero or one.
  3. Because * and ? allow zero occurrences, they can produce empty matches.
  4. An empty match is a valid match whose matched text is "".
  5. An empty output after the dots does not mean that the current non-a character matched. It means zero a characters matched at that position.
  6. The Regex engine can also produce a zero-length match at the end position of the string. For a string of length 9, the final end position is index 9, even though the final character itself is at index 8.
  7. {m} specifies an exact number of occurrences, and {m,n} specifies a range from minimum m to maximum n.
  8. Quantifiers are greedy by default unless changed using other Regular Expression syntax.
  9. The target string used for the main Quantifier examples is abaabaaab.
  10. Understanding empty matches is especially important before learning the major functions of Python's re module.
📝 Key Takeaways
  • Quantifiers specify how many times a particular character or pattern should occur
  • a+ means at least one a, while a* means zero or more a
  • a? matches zero or one a, and a{m,n} matches between m and n occurrences
  • a* and a? produce empty matches because their minimum required occurrence is zero
  • Quantifiers such as +, *, and {m,n} are greedy by default - they match as many characters as possible

🧠 Test Your Knowledge

10 Questions
Progress: 0 / 10