Concepts / Using re.search() and re.findall()

Using re.search() and re.findall()

Parentheses in regular expressions mark the portion of a match that findall() should extract and return.

  • Programming

One Match, Two Possible Results

A regular expression can match a larger piece of text while you keep only a smaller piece from inside it. The key tool for controlling what findall() returns is the capturing group: the part of the pattern enclosed in parentheses. Parentheses do not change the text that the pattern matches. They change which part of that match findall() returns.

findall()findall()X-:42pattern without parenthesesX-:42returned textX-:(42)pattern with a capturinggroup42captured text
Which text does findall() return when the same matching pattern is written with and without a capturing group?

Tracing a Capturing Group

What do you think happens?

Suppose findall() uses the pattern X-:(\d+) on the text X-:42. What should the result contain: the full text X-:42 or only the number 42?

  • The full text X-:42
  • Only the text 42
Reveal answer

Answer: Only the text 42

The complete pattern must match X-:42, but the parentheses identify 42 as the portion to extract. With one capturing group, findall() returns a list of strings containing the captured text.

import re text = "X-:42" values = re.findall(r"X-:(\d+)", text) print(values)

Output
['42']

Matching Versus Extracting

Think of the regular expression as having two jobs. First, the whole pattern decides whether the relevant text matches. Everything outside the parentheses still participates in that decision. Second, the parentheses identify the portion to keep when findall() builds its result. Therefore, a pattern can match a long string but return only a short substring from inside it.

capture 42capture 17capture 8X-:42complete matching text["42", "17", "8"]captured stringsX-:17complete matching textX-:8complete matching text
How does findall() transform several complete matches into a list containing only the text inside the capturing group?

When several pieces of text match, findall() collects the extracted results. With one capturing group, the result is a list of strings. Without parentheses, the returned strings are the entire matching substrings instead. The matching process can therefore be the same while the returned result has different content.

Choosing search() or findall()

first matching locationevery matching resultre.search()match objectre.findall()list of results
Does the operation give you the first matching location and match object, or a collection of extracted results from every match?
OperationResult focusUseful distinction
re.search()The first matching location and a match objectUse this when the question concerns whether and where a first match occurs.
re.findall()A collection of extracted resultsUse this when the question concerns all matching portions and the text captured by parentheses.

The capturing-group rule in this article applies specifically to what findall() returns. re.search() is described differently: it gives the first matching location and a match object. Do not assume that choosing parentheses automatically makes both functions return the same kind of result.

Working with Extracted Values

Extracting Two Parts from Each Line

A pattern must identify lines beginning with X-, followed by a colon, a label, and a number. Capture the label and number.

Build the pattern: Use two sets of parentheses so the label and number are separate captured portions. The surrounding characters still have to match.

Apply findall(): Because the pattern has more than one capturing group, findall() returns a list of tuples. Each tuple contains the captured values in the order of the groups.

Interpret the values: The captured number is still a string. Convert it to int when arithmetic or numeric comparison is needed.

A list of tuples, with each tuple containing the captured label and number in pattern order.

python
Output
[('score', '42'), ('level', '7')]
score 43
level 8

The first result is a tuple because the pattern has two capturing groups. The tuple values are strings, including the value that looks like a number. Calling int() creates an integer for the later addition. Similarly, extracted decimal values need conversion to float when floating-point operations are required.

Mistakes Beginners Make

  • Expecting findall() to return the whole match when a capturing group is present.

    Parentheses tell findall() to return only the enclosed portion.

    Fix: Separate the idea of matching the complete pattern from extracting the captured portion.

  • Assuming captured numbers are already numeric.

    findall() returns extracted values as strings.

    Fix: Convert the value explicitly with int() or float() when the later operation requires that type.

  • Treating multiple capturing groups like one capturing group.

    findall() represents multiple captured values from each match as a tuple.

    Fix: Unpack each tuple in the same order as the capturing groups appear in the pattern.

  • Indexing the result without checking whether anything matched.

    There is no element to access when findall() returns no matches.

    Fix: Verify that the returned list is non-empty before accessing one of its elements.

Practice: Predict the Result

EASY

Consider the pattern X-:(\d+) applied to the text X-:12 X-:30. Predict the result of findall() before checking the answer. Then state what conversion is needed if you want to add 5 to each extracted value.

Hints
  • The number is inside the only capturing group.
  • One capturing group produces a list of strings.
  • Convert each string to int before performing integer arithmetic.

Practice Check

Determine the result shape for X-:(\d+) applied to X-:12 X-:30, and identify the conversion needed for addition.

Identify the group: The digits are inside one pair of parentheses, so only the digit text is captured.

Identify the result shape: One capturing group produces a list of strings.

Prepare for arithmetic: Convert each extracted string to int before adding 5.

The result is ['12', '30']; convert the strings with int() before arithmetic.

Key Takeaways

  1. The complete regular expression must match, but parentheses identify the part findall() should return.
  2. With one capturing group, findall() returns a list of strings.
  3. With multiple capturing groups, findall() returns a list of tuples in group order.
  4. Extracted values are strings, so convert them to int or float when numeric work is needed.
  5. Check that findall() returned a non-empty list before accessing an element.
  6. re.search() focuses on the first matching location and a match object, while re.findall() provides a collection of extracted results.

Key Takeaways

  • Parentheses create capturing groups that control what findall() extracts.
  • The surrounding pattern still determines whether the complete match succeeds.
  • One group produces strings; multiple groups produce tuples.
  • Convert extracted strings before numeric operations, and check for an empty result first.
  • Use the distinction between re.search() and re.findall() to choose between a first match object and a collection of extracted results.