Using re.sub() to Replace Matches
re.findall() is a Python method that extracts all non-overlapping substrings matching a regex pattern from a string.
A Different Kind of Match Operation
The supplied topic title mentions re.sub(), but the concept covered here is re.findall(). The key difference is the kind of result you are trying to obtain: findall() extracts matching text from a string, while this lesson concentrates on how those extracted matches are located and returned.
re.findall() is a Python method that extracts all non-overlapping substrings matching a regular expression pattern from a string.
The two questions to ask first are: Which parts of the input match the pattern, and how many capturing groups does the pattern contain?
How findall() Moves Through Text
findall() scans from left to right. It searches for the first location where the pattern can match. After it finds a match, it records the result and resumes searching at the position immediately after that match ends. Because the next search begins after the previous match, the results are non-overlapping.
['42', '17', '93']In this example, the pattern identifies each digit sequence. findall() records the first sequence, continues after it, then records the next sequence. The result is a list containing three strings rather than one combined match.
Capturing Groups Change the Result
A capturing group is a subpattern enclosed in parentheses. The number of capturing groups controls the structure of the value returned by findall(). This is independent of how many matches are found.
| Capturing groups | Returned structure | What each result contains |
|---|---|---|
| None | List of strings | Each complete match |
| Exactly one | List of strings | The content of that one group |
| Two or more | List of tuples | The captured groups in order |
The number of capturing groups determines the shape and contents of the findall() result.
import re
text = "A42 B17"
complete = re.findall(r"[A-Z]\d+", text)
letter = re.findall(r"([A-Z])\d+", text)
pair = re.findall(r"([A-Z])(\d+)", text)
print(complete)
print(letter)
print(pair)- 1The pattern with no capturing groups returns each complete match.
- 2The pattern with one capturing group returns only the contents of that group for each match.
- 3The pattern with two capturing groups returns one tuple per match, with the captured values in group order.
['A42', 'B17']
['A', 'B']
[('A', '42'), ('B', '17')]Several Matches from One Line
A single input line can contain several occurrences of the same pattern. findall() does not stop after the first successful match. It continues scanning from the end of each match and collects every later non-overlapping match that fits the pattern.
Extracting addresses from one log line
Extract each email address from the line "Contact a@example.com or b@example.com".
- 1Choose a pattern
Use a pattern that matches the email-address form used in this example.
- 2Scan left to right
findall()locates the first address, records it, and resumes immediately after that address. - 3Continue scanning
The method finds the second address and records it as another result.
- 4Check the structure
Because the example pattern has no capturing groups, the result is a list of strings containing complete matches.
The result contains two extracted email-address strings.
Diagnosing Unexpected Results
When findall() produces a result that differs from your expectation, do not inspect only the returned list. Inspect the pattern and the match process that produced it. Three especially important checks are the pattern itself, the number of capturing groups, and the boundaries created by quantifiers.
Common mistake Expecting complete matches when the pattern contains one capturing group.
What it looks likeA pattern such as ([A-Z])\d+ returns the captured letter rather than the complete letter-and-digit match.
Why it failsWith exactly one capturing group,
findall()returns the contents of that group.Do this insteadCount the parentheses that create capturing groups and decide whether you want the full match or a captured subpattern.
Common mistake Expecting a flat list when the pattern contains multiple capturing groups.
What it looks likeA pattern such as ([A-Z])(\d+) returns pairs such as ('A', '42').
Why it failsTwo or more capturing groups make
findall()return a list of tuples.Do this insteadRead each tuple as the captured groups in their pattern order.
Common mistake Assuming the pattern matches different boundaries than it actually does.
What it looks likeA quantifier may include more or fewer characters than expected.
Why it failsQuantifiers affect how much text a match consumes. By default, quantifiers are greedy.
Do this insteadTest the pattern in isolation and inspect whether its quantifiers should be greedy or lazy.
Common mistake Assuming findall() overlaps matches.
What it looks likeAfter recording a match, the next search does not begin inside that match.
Why it failsfindall()resumes after the previous match ends.Do this insteadTrace the end of each match and begin the next search from that position.
Debug in a fixed order: first test whether the pattern matches the intended text, then count the capturing groups, then inspect quantifiers and their match boundaries. This separates a pattern problem from a return-structure problem.
Practice the Prediction
What do you think happens?
What does the result contain, and what is its structure? Assume the input is "A42 B17" and the pattern is ([A-Z])(\d+).
Reveal answer
A list of tuples containing two captured strings per match: [('A', '42'), ('B', '17')].
The pattern has two capturing groups. findall() returns a tuple for each non-overlapping match, with the groups in order.
Write three findall() patterns for the text "A42 B17": one with no capturing groups, one with exactly one capturing group, and one with two capturing groups. Predict the result structure before checking your code.
Hints
- Use a pattern that identifies an uppercase letter followed by digits.
- For the one-group version, place parentheses around only the part you want returned.
- For the two-group version, place separate capturing groups around the letter and the digits.
Key Takeaways
- 1
findall()scans from left to right and extracts all non-overlapping matches. - 2After each match, scanning resumes at the position where that match ends.
- 3With no capturing groups,
findall()returns complete matches as strings. - 4With one capturing group,
findall()returns the contents of that group as strings. - 5With multiple capturing groups,
findall()returns tuples containing the captured groups in order. - 6Unexpected results usually require checking the pattern, capturing-group count, quantifiers, and greedy or lazy behavior.
At a glance
re.findall()extracts all non-overlapping substrings that match a regular expression.- The method scans left to right and resumes after each completed match.
- The number of capturing groups determines whether the result contains complete strings, captured strings, or tuples.
- Debug unexpected output by checking the pattern, capturing groups, quantifiers, and match boundaries.