re.finditer

findall with the full story: each item is a Match, so you keep the positions, every group and groupdict() — and matches are produced one at a time. Also available as Pattern.finditer(string, pos, endpos).

re functionPython 3 (all)Live demo
Common call
for m in re.finditer(r'\d+', text): ...
Returns
an iterator of re.Match objects (not a list)
Replaces
findall when you also need where each match is
Watch out
It is an iterator: it is used up after one loop — wrap in list() to reuse
re.finditer(patternpattern — The regular expression, as a raw string.type: str | re.Pattern · required, stringstring — The text to scan.type: str · required, flagsflags — Flags such as re.IGNORECASE. Not allowed with a compiled pattern.type: re.RegexFlag | int · default: 0=0)
→ Iterator[re.Match]

Demo

Live evaluation
Each Match object knows its text and its (start, end) position.
Try:
Inputs
patternstra regular expression
textstrtext to scan
Code
import re
[(m.group(), m.span()) for m in re.finditer('\\w+', 'to be or not')]
Result
[('to', (0, 2)), ('be', (3, 5)), ('or', (6, 8)), ('not', (9, 12))]

span() is (start, end) with end exclusive, so text[start:end] is the match. With a pattern that can match nothing, like -*, you also get empty matches: one before a, the '--' run, then empty matches before b and at the end. A named group that did not participate shows as None in groupdict() — unlike findall, which would give ''.

Parameters

NameTypeRequiredDescription
patternstr | re.PatternyesThe regular expression, as a raw string.
stringstryesThe text to scan.
flagsre.RegexFlag | intno (0)Flags such as re.IGNORECASE. Not allowed with a compiled pattern.

Return value

Iterator[re.Match] — A lazy iterator yielding one Match object per match, left to right.

Common patterns

Highlight or annotate matches by position
Slices between matches come from m.start() and m.end().
import re
out, last = [], 0
for m in re.finditer(r'\d+', text):
    out.append(text[last:m.start()])
    out.append(f'[{m.group()}]')
    last = m.end()
out.append(text[last:])
result = ''.join(out)
Parse records into dicts
Named groups + groupdict().
import re
LINE = re.compile(r'(?P<level>[A-Z]+) (?P<msg>.*)')
records = [m.groupdict() for m in LINE.finditer(log_text)]
Stop at the first N matches
The iterator is lazy; itertools.islice stops scanning early.
import re
from itertools import islice
first_three = [m.group() for m in islice(re.finditer(r'\w+', huge_text), 3)]

Examples

1. Text and position
import re [(m.group(), m.start()) for m in re.finditer(r'\d+', 'a1 b22 c333')]
Returns
[('1', 1), ('22', 4), ('333', 8)]
2. An iterator, not a list
import re it = re.finditer(r'\d', 'a1b2') next(it)
Returns
<re.Match object; span=(1, 2), match='1'>
3. Used up after one pass
import re it = re.finditer(r'\d', 'a1b2') (len(list(it)), len(list(it)))
Returns
(2, 0)
4. Named groups as dicts
import re [m.groupdict() for m in re.finditer(r'(?P<k>\w+)=(?P<v>\d+)', 'x=1 y=2')]
Returns
[{'k': 'x', 'v': '1'}, {'k': 'y', 'v': '2'}]
5. Whole match despite groups
import re [m.group(0) for m in re.finditer(r'(\w)(\d)', 'a1 b2')]
Returns
['a1', 'b2']
6. No match: empty iterator
import re list(re.finditer(r'z', 'abc'))
Returns
[]

Pitfalls

1. Iterating twice
The second loop sees nothing. Materialize with list() if you need the matches more than once.
reuse iterator
import re
ms = re.finditer(r'\d', 'a1b2')
first = [m.group() for m in ms]
[m.group() for m in ms]
[]
list() once
import re
ms = list(re.finditer(r'\d', 'a1b2'))
first = [m.group() for m in ms]
[m.group() for m in ms]
['1', '2']
2. Treating the result like a list
finditer returns an iterator object; printing it shows only its type and address, and it has no len() or indexing.
the raw result
import re
type(re.finditer(r'\d', 'a1')).__name__
'callable_iterator'
list(it)
import re
list(re.finditer(r'\d', 'a1'))
[<re.Match object; span=(1, 2), match='1'>]

When to use

Use it
  • You need positions (start, end, span) of each match
  • Named groups per match — groupdict() records
  • Large inputs where you may stop early (lazy)
Reach for something else
  • Just the strings → re.findall is shorter
  • Only the first match → re.search

Notes

CPython impl
Implemented with an internal scanner object (Pattern.scanner): each next() calls its search() and resumes after the previous match
Empty matches
An empty match is followed by a match attempt at the same position that must not be empty (Python 3.7+ rule)
Laziness
Matches are found one at a time as you iterate, so breaking out of the loop early skips the rest of the scan

FAQ

findall returns a list of strings (or group tuples); finditer returns an iterator of Match objects. Use finditer when you need positions, named groups, or to avoid building a big list.