Regex tester · Guide

Regex Match Count: Counting Occurrences and Testing Containment

Counting matches and checking whether a match exists are different operations with different costs. Counting also has a failure mode that testing does not: a pattern that can match nothing at all.

Counting

JavaScript
// JavaScript — g flag required
(str.match(/\d+/g) || []).length;
[...str.matchAll(/\d+/g)].length;

# Python
len(re.findall(r'\d+', s))

# PHP
preg_match_all('/\d+/', $s);   // returns the count

# shell — -o prints each match on its own line
grep -o '[0-9]\+' file | wc -l
grep -c 'pattern' file          # counts LINES, not matches

grep -c counting lines rather than matches is a classic discrepancy — a line with three matches counts once.

Containment is cheaper

To answer "does this contain a match", use test or search. They stop at the first hit; counting scans the entire input. On large strings in a hot path that difference is real.

It also expresses intent. if (count > 0) and if (test(...)) behave the same and read differently — the second says what you meant.

The zero-width trap

A pattern that can match an empty string — \d*, a?, ^ — matches at every position, so counts are surprising and manual loops hang:

JavaScript
'abc'.match(/\d*/g)      // ['', '', '', ''] — four empty matches

If you write your own exec loop with the g flag, a zero-width match never advances lastIndex and the loop runs forever. matchAll handles this internally; hand-rolled loops must bump lastIndex themselves.

The fix is usually the pattern: require at least one character with + instead of *.

Overlapping matches

Standard matching does not overlap: after a match the engine resumes at its end, so aa in aaaa counts twice, not three times.

To count overlapping occurrences, use a lookahead that consumes nothing — (?=(aa)) — and count the captures. Each position is tested independently, so aaaa gives three.

Frequently asked questions

Why does grep -c give a smaller number than expected?

It counts matching lines, not matches. Use grep -o piped to wc -l.

Why does my count include empty strings?

The pattern can match zero characters. Use + instead of * where a match must consume something.

Should I count matches to test containment?

No. Use test or search — they stop at the first match.

Ready to try it?

Open the free browser-based Regex tester and apply what you just read — no sign-up, runs locally.

Open the Regex tester tool