Pattern Matching with re.search() and re.match()
re.findall() is a Python method that extracts all non-overlapping substrings matching a regex pattern from a string.
Try it: Regular expressions: a backtracking matcher
How Python's re module matches a pattern: it tries each start position, matches tokens one by one, lets greedy quantifiers take as much as possible and give it back when the rest fails (lazy ones take as little as possible), tries alternatives in order and records groups.
How it works
- search tries start positions 0, 1, 2 …; match tries only position 0; findall collects every non-overlapping match.
- Tokens are matched left to right: literals, ., classes like [a-c] and \d, and zero-width anchors ^, \$, \b.
- A greedy quantifier (* + ? {m,n}) takes as many repetitions as it can, then backtracks one at a time; a lazy one (*? +? ??) takes as few as possible and grows only when needed.
- Alternatives in a | b are tried in order; the first that lets the whole pattern match wins.
- Parentheses capture the text their part matched; captures are undone when the matcher backtracks past them.
Default run (63 steps): re.search('(\\w+)@(\\w+)\\.com', 'mail bo@ikb.com now'): try each start position until one matches. … re.search('(\\w+)@(\\w+)\\.com', 'mail bo@ikb.com now') → <re.Match object; span=(5, 15), match='bo@ikb.com'>
Simplified: A subset of re: no flags, lookarounds, backreferences, named groups or possessive quantifiers; pattern and text up to 24 printable ASCII characters. The trace stops after 1900 attempts ("too many steps") where real Python would keep backtracking.
Loading the simulation…
One Line, Many Matches
When a string contains several occurrences of the same pattern, re.findall() collects them into one result. It scans the string from left to right, records each non-overlapping match, and returns the collected results. The important question is not only which text matches, but also how the regular expression is grouped, because capturing groups determine the shape of the returned value.
The Left-to-Right Scan
Imagine findall() moving through a line from position 0 onward. It scans forward until it finds text that can begin a match. After the pattern matches, findall() records that result and resumes scanning at the position immediately after the match ends. Because the next scan begins after the completed match, a later result cannot overlap the earlier one.
Tracing repeated matches
Suppose a pattern matches the two-character sequence ab in the generated text ababa.
- 1First scan
The first ab is found and recorded.
- 2Resume position
Scanning resumes after that complete match, so the next search begins after the first ab.
- 3Second scan
The later ab is found and recorded. The possible match beginning inside the first match is not returned because
findall()produces non-overlapping matches.
The result contains the two non-overlapping occurrences identified by the scan.
Capturing Groups Shape Results
A capturing group is a set of parentheses that creates a subpattern. The number of capturing groups controls what each item in the findall() result represents. With no capturing groups, findall() returns a flat list of strings containing the complete matches. With exactly one capturing group, it still returns a flat list of strings, but each string contains only the content captured by that group. With two or more capturing groups, it returns a list of tuples, and each tuple contains the captured groups in their order.
| Capturing groups | Returned item | Meaning |
|---|---|---|
| None | String | The complete match |
| Exactly one | String | Only the content of that group |
| Two or more | Tuple | The captured groups in order |
The return structure depends on the number of capturing groups.
Collecting Repeated Occurrences
A practical use of findall() is extracting every occurrence of a repeated pattern from one line, such as several email addresses in an email log line. The method scans toward the first possible address, records the complete match when the pattern succeeds, then continues after that match until it finds the next one. The final result contains all matches that the pattern identified without overlap.
Consider a generated log line containing three email addresses. If the regular-expression pattern matches an entire email address and contains no capturing groups, findall() returns a flat list of the three complete address strings. If the same pattern is reorganized into two capturing groups, each result becomes a tuple containing the two captured parts in order. The text being scanned is the same; the result structure changes because the pattern's groups changed.
Diagnosing Unexpected Output
When findall() returns something different from what you expected, inspect the pattern before changing the surrounding program. First check whether the pattern actually matches the intended text. Next count its capturing groups, because adding or removing a group changes the return structure. Then inspect quantifiers such as *, +, and ?, which may match more or fewer characters than intended. By default, quantifiers are greedy and match as much as possible; adding ? after a quantifier makes it lazy.
Common mistake Expecting complete matches when the pattern has one capturing group
What it looks likeA pattern includes one parenthesized subpattern, but the result contains only the text captured by that group.
Why it failsWith exactly one capturing group,
findall()returns the group's content rather than the entire match.Do this insteadCount the capturing groups and decide whether the group content or the complete match is needed.
Common mistake Expecting strings when the pattern has multiple capturing groups
What it looks likeA pattern contains two or more parenthesized subpatterns, but the result is inspected as though every item were one string.
Why it failsTwo or more capturing groups cause
findall()to return a list of tuples.Do this insteadTreat each tuple as the captured groups in their pattern order.
Common mistake Assuming findall() returns overlapping matches
What it looks likeA possible match begins inside a match that was already recorded.
Why it failsfindall()resumes scanning after the previous match ends, so overlapping results are not returned.Do this insteadTrace the end of each recorded match and identify the position where the next scan begins.
Common mistake Ignoring greedy or lazy quantifiers
What it looks likeA quantifier matches more or fewer characters than expected.
Why it failsQuantifiers affect how much text the pattern consumes. By default they are greedy; adding ? after a quantifier makes it lazy.
Do this insteadInspect *, +, and ? and verify whether the pattern should consume as much as possible or use lazy behavior.
- 1Test the regular-expression pattern against the text you believe it should match.
- 2Count the capturing groups in the pattern.
- 3Check whether the pattern matches complete text or only captured subparts.
- 4Inspect *, +, and ? for unintended match lengths.
- 5Check whether the expected matches overlap;
findall()returns non-overlapping results.
Check Your Prediction
A generated pattern finds two occurrences in a line. In the first version, the pattern has no capturing groups. In the second version, it has two capturing groups. Predict the result structure in each version: will each result be a string or a tuple, and will the complete match or captured parts be represented?
Hints
- Start by counting the capturing groups.
- Remember that zero groups and one group produce lists of strings.
- Remember that two or more groups produce lists of tuples.
Checking the return structure
Determine the result shape for two non-overlapping matches when the pattern has no groups, then determine it when the pattern has two groups.
- 1No groups
The result is a flat list of strings, with each string representing a complete match.
- 2Two groups
The result is a list of tuples, with each tuple containing the two captured groups in order.
- 3Match count
The number of collected occurrences is based on the non-overlapping scan, while the item structure is based on the capturing-group count.
The scan determines how many results are collected; the capturing groups determine whether each result is a string or a tuple and what it contains.
Key Takeaways
- 1
re.findall()scans a string from left to right and extracts all non-overlapping matches. - 2After recording a match, scanning resumes at the position after that match ends.
- 3With no capturing groups,
findall()returns complete matches as strings. - 4With one capturing group,
findall()returns the captured content as strings. - 5With two or more capturing groups,
findall()returns tuples containing the captured groups in order. - 6Unexpected results usually require checking the pattern, group count, quantifiers, and intended match boundaries.
At a glance
findall()collects every non-overlapping match it can identify while scanning left to right.- The number of capturing groups controls the structure and contents of each returned item.
- Zero or one capturing group produces strings; two or more produce tuples.
- Debugging starts by checking the pattern, then its groups, quantifiers, and match boundaries.