Concepts / Using re.findall() to Extract Multiple Matches

Using re.findall() to Extract Multiple Matches

If a match begins at index 0 and ends at index 5, the matching slice uses positions 0 through 4. This is why the end position can be used directly as the second boundary in a Python slice.

  • Programming
Interactive lab

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

  1. search tries start positions 0, 1, 2 …; match tries only position 0; findall collects every non-overlapping match.
  2. Tokens are matched left to right: literals, ., classes like [a-c] and \d, and zero-width anchors ^, \$, \b.
  3. 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.
  4. Alternatives in a | b are tried in order; the first that lets the whole pattern match wins.
  5. 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.

Educational simulation

Loading the simulation…

One Match or Many

A search task can have two different goals. You may want to know whether a pattern appears and inspect the first match, or you may want to collect every matching piece. Python's re module supports both situations. re.search() scans from left to right and stops when it finds the first match. re.findall() is used when the goal is to extract multiple matching pieces. Understanding the difference prevents a program from silently returning less information than you intended.

re.search()first matching resultmatch objectone matchre.findall()multiple matching piecesmatching piecesall matchesreturnsextracts
How does re.findall() return all matching pieces while re.search() returns only the first match?

The Search Result

Calling re.search(pattern, string) does not simply produce True or the matched text. When the pattern appears, it returns a match object. That object contains information about the match, including the matched text, its starting index, its ending index, and the original string. Use group() to retrieve the matched text, start() to retrieve where it begins, and end() to retrieve the position immediately after it ends.

match objectresult of re.search()matched textgroup()starting indexstart()ending indexend()original stringsource textcontainscontainscontainsrecords
What information does a match object contain, and how do its matched text and positions relate to the original string?
Worked example

Reading a First Match

Suppose the string is From: Ada and the searched pattern is From:.

  1. 1
    Search

    re.search() scans the string from left to right and stops when it finds the first occurrence of the pattern.

  2. 2
    Inspect the text

    The returned match object's group() method gives the text that matched the pattern.

  3. 3
    Inspect the location

    The start() method gives the index of the first matched character, while end() gives the index immediately after the final matched character.

  4. 4
    Check the slice

    Using the original string from start() through end() with Python's slice convention produces the same matched text.

Result

The match object identifies both what matched and where it occurs; it is more informative than a simple True result.

Mapping Positions to Text

Match positions use Python's zero-based indexing. The first character has index 0, and the end position follows Python's slice convention: it points one position after the last character in the match. Therefore, original_string[match.start():match.end()] produces the matched text. The end position is not the index of the last matched character; it is the boundary where the match stops.

Findex 0rindex 1oindex 2mindex 3:index 4end positionindex 5end() boundary
Where is the matched text located in the original string, and how do start() and end() identify it?
Key point

If a match begins at index 0 and ends at index 5, the matching slice uses positions 0 through 4. This is why the end position can be used directly as the second boundary in a Python slice.

When No Match Exists

When the pattern never appears, re.search() returns None instead of raising an error. None is falsy, so a conditional check can use the result to decide whether the match-handling path should run. The essential debugging rule is to check the result before calling group(), start(), or end(). Calling one of those methods on None causes an AttributeError because None is not a match object.

re.search()search patternresultmatch object or Nonematch objectuse group(), start(), end()Nonedo not call match methodsreturnspattern foundno match
What control-flow path should the program take when re.search() returns None instead of a match object?
  • Common mistake

    Calling group() immediately without checking the search result.

    What it looks like

    A search finds no occurrence, returns None, and the program then attempts to call group() on that result.

    Why it fails

    None does not provide match object methods, so the call causes an AttributeError.

    Do this instead

    Check whether the result is truthy or whether it is None before calling group(), start(), or end().

  • Common mistake

    Treating re.search() as if it returned only True or False.

    What it looks like

    A condition confirms that a match exists, but the program then ignores the returned object.

    Why it fails

    The result contains the matched text and its location, not just a yes-or-no answer.

    Do this instead

    Store the result when you need to inspect group(), start(), or end().

Choosing the Right Search Tool

Use re.search() when the search requires a pattern rather than a simple literal string. Regular expressions are appropriate when you need to find any digit, any word character, a repeating pattern, or text matching one of several alternatives. For an exact fixed string, methods such as str.find() or the in operator are simpler and faster.

exact fixed stringliteral textfind() or insimpler and fasterpatterndigits or repetitionre.search()regular expressionuseuse
When should a program use re.search() for a pattern, and when is a simpler string method sufficient?
TaskSuitable choiceReason
Check for an exact fixed stringstr.find() or the in operatorThe search is literal, so a regular expression is unnecessary.
Find a pattern involving digits, word characters, repetition, or alternativesre.search()The task requires pattern matching.
Extract every matching piecere.findall()The goal is multiple matches rather than the first match.

Choose the least complicated tool that expresses the search you actually need.

Practice and Review

MEDIUM

A program searches a string for a pattern and may receive either a match object or None. Describe the checks you would perform before using group(), start(), and end(). Then decide whether re.search(), str.find(), or the in operator is the clearest choice for an exact fixed string.

Hints
  • A successful search returns a match object.
  • A failed re.search() call returns None.
  • An exact fixed string does not require regular-expression pattern matching.
  1. 1
    re.search() scans from left to right and returns the first matching result as a match object. The object's group(), start(), and end() methods reveal the matched text and its location. If no match exists, the return value is None, so check it before calling match methods. Use re.findall() when you need multiple matching pieces, and prefer str.find() or the in operator for a simple fixed-string search.

Key Takeaways

  • re.search() returns a match object for the first matching occurrence and None when no match exists.
  • A match object provides the matched text through group(), the starting index through start(), and the ending boundary through end().
  • The start and end positions follow Python's zero-based indexing and slice convention.
  • Check for None before calling match object methods to avoid an AttributeError.
  • Use re.findall() for multiple matches, re.search() for pattern-based searching, and simpler string methods for exact fixed strings.

Related concepts

More concepts starting with U