What it does
REGEXEXTRACT(text, pattern, [return_mode], [case_sensitivity])
REGEXEXTRACT searches text for the regular expression pattern and returns what it matched.
Unlike its siblings it is not one function but three, selected by return_mode, and the three
differ in shape as well as in content:
That table is the whole function, and each row deserves a sentence.
Mode 0 performs a leftmost search and returns the matched substring. Not the whole text, not
the position — the matched text itself.
Mode 1 returns all non-overlapping matches. This is the mode that makes REGEXEXTRACT a
dynamic-array function: the result spills, and its length depends on the data, which is exactly the
case that dynamic arrays were introduced for. The scan order and the non-overlap rule are standard
regex semantics (each search resumes after the previous match), though nothing consulted here
states how a zero-width match is advanced past — a real question, since a pattern that can match
empty would otherwise loop.
Mode 2 returns the capture groups of the first match. This is the mode that turns a regex into
a parser: one pattern with parenthesised groups splits a string into its parts in a single call.
Note what it does not say — whether group 0 (the whole match) is included, whether unmatched
optional groups appear as empty strings or #N/A, and what the shape is (row or column). None of
those is settled here.
The dialect is PCRE2, documented on the family's REGEXTEST page. And one further documented note
matters more than it looks: REGEXEXTRACT always returns text values. Extracting "123" gives
you the string "123", and Microsoft's page explicitly points you at VALUE to convert it — which
drags in every locale question on the VALUE page. For numeric extraction,
NUMBERVALUE with explicit separators is the more portable second step.
Arguments
REGEXEXTRACT(text, pattern, [return_mode], [case_sensitivity])
Two argument positions cause trouble.
return_mode is the position readers get wrong when porting from other spreadsheet dialects, where
the equivalent function's third argument means something else entirely. It is a shape selector:
changing it changes whether you get a scalar or an array, which changes whether the formula spills,
which changes whether it fits where you put it.
case_sensitivity has the same inverted-looking polarity as elsewhere in the family: 0 is
case-sensitive. It also overlaps with PCRE2's inline (?i) flag, and nothing consulted here
states which wins if they disagree.
Quirks
Result and edge cases
The return kind depends on return_mode: Text in mode 0, Array of text in modes 1 and 2.
Array results are published by the host-side adaptation step described in
the call pipeline, which is also where #SPILL! comes from.
- Everything comes back as text. Documented, and worth repeating because it is the most common
source of downstream surprise.
- Non-text arguments coerce to text by the ordinary rules in
coercion and lifting; the generated battery rows where a
number is passed as both
text and pattern are coercion artefacts, not regex behaviour.
- Errors propagate ahead of matching.
- An empty referenced cell delivers
Empty, distinct from an omitted argument; see
the value universe.
- Array-valued
text — the obvious way to extract from a column — is not asserted here, and it
interacts badly with modes 1 and 2 in any case: an array of arrays is not a value this model
admits (see the value universe), so lifting cannot be
unconditional.
Errors
Microsoft's page publishes no explicit error table for this function (confirmed on the retrieved
page). What can be stated:
Over an array, TEXTSPLIT and REGEXEXTRACT keep only the first piece of each element
When TEXTSPLIT's text is an array, even a 1 by 1 array, the call lifts over the elements and each returns only its first piece. MAP over the same lambda gives #CALC!, while BYROW gives the first pieces. REGEXEXTRACT lifted over return_mode does the same, and mode 2 on a pattern with no capture group gives #VALUE!.
Inputs, build details and evidence