=FormulaProof
1

Finding ·

Google Sheets quietly deletes the umlaut from Müller

2

Here is a formula that appears in a lot of spreadsheets. It removes punctuation from a name:

=REGEXREPLACE("Müller", "[^\w]", "")

Delete everything that is not a word character. Run it in a real Google Sheet, which we did, and the cell returns:

Mller

No error. No warning. The umlaut is gone, and so is the person’s name.

What we ran

Twenty-four formulas, in a live Google Sheet, against names and places that exist: Müller, José Álvarez, Zürich, İstanbul, 김재성, naïve, Straße. The kind of data that shows up the first time a customer list crosses a border.

Google’s documentation says the spreadsheet runs RE2, “except Unicode character class matching.” One clause, no examples. We went to find out what it costs.

The answer is stranger and more specific than “Sheets is bad at Unicode.”

The engine understands characters perfectly well

Three measurements, all TRUE, all surprising if you expected a broken engine:

=REGEXMATCH("é", "^.$")                →  TRUE
=REGEXEXTRACT("Straße", "^.{3}")       →  Str
=REGEXMATCH("MÜLLER", "(?i)müller")    →  TRUE

A dot matches é — one character, not the two bytes it occupies. ^.{3} on Straße returns Str, counting characters, not bytes. And case-insensitive matching folds Ü to ü correctly, which is a genuinely Unicode-aware operation.

This engine reads text. It knows what a character is.

And then it will not admit that ü is a letter

=REGEXMATCH("Müller", "^\w+$")     →  FALSE
=REGEXMATCH("김재성", "^\w+$")      →  FALSE
=REGEXMATCH("naïve", "^[a-z]+$")   →  FALSE
=REGEXMATCH("café au lait", "\bcafé\b")  →  FALSE

\w means word character, and in Google Sheets it means [A-Za-z0-9_] and nothing else. Not ü. Not é. Not a single Korean syllable. \b, the word boundary, is built out of the same definition, so it finds a boundary in the middle of café.

The same holds for digits. These are both FALSE:

=REGEXMATCH("123", "^\d+$")   →  FALSE     (full-width, from a Japanese or Korean keyboard)
=REGEXMATCH("١٢٣", "^\d+$")    →  FALSE     (Arabic-Indic)

If a colleague pastes a column of full-width numbers out of an exported Excel file — which happens — every validation formula built on \d will say those cells contain no digits, and every one of them will say it politely.

The extraction that returns one letter

Put those two facts together and you get the failure that will actually reach a report.

=REGEXEXTRACT("Zürich, CH", "^([A-Za-z]+)")

This is a formula somebody wrote to pull the city out of a location column, and it works. On Boston. On Leeds. On Zurich, if the list came from a system that had already thrown the umlaut away.

On Zürich, CH it returns:

Z

Not #N/A. Not #REF!. The letter Z, in a city column, in a row that will be summed and filtered and sent on. ^(\w+) behaves identically, for the same reason. And on a name:

=REGEXEXTRACT("José Álvarez", "^\w+")   →   Jos

The pattern matched. There was a run of ASCII letters at the start of the string. The engine returned it, correctly, and stopped where the accent began.

The correct fix is the one thing that fails loudly

Every regex engine written this century has an answer for this. It is \p{L}any character that Unicode calls a letter. It is what you are supposed to write.

=REGEXMATCH("Müller", "^\p{L}+$")   →   #REF!
=REGEXMATCH("김재성", "^\p{L}+$")    →   #REF!

This is what Google’s sentence means. Unicode character classes are the thing that is missing, and \p{L} is the construct that names them.

So the position is this. The correct tool throws an error. The incorrect tool returns a wrong answer in silence. A person who reaches for \p{L}, sees #REF!, and retreats to \w has been led, step by step, from a formula that would have worked to one that quietly mangles names.

#REF!, incidentally, is the same error Sheets gives you for a deleted cell reference, which is not a hint that your pattern is unsupported. We measured that separately, in the RE2 limits run.

Why the tester you used did not warn you

Almost every regex tester on the web runs the browser’s own engine, which is JavaScript. And in JavaScript, \w is also [A-Za-z0-9_]. So when you tried ^\w+$ against Müller in a tester, it said false — and it was right, and you probably concluded you had made a typo rather than that the shorthand does not mean what its name says.

Then you looked up the fix, found \p{L}, and tried it. JavaScript supports \p{L} when the pattern carries the u flag, which testers set for you. It matched. Green tick.

You pasted it into Google Sheets and got #REF!.

The tester was not lying about JavaScript. It was answering a question about a different program. That gap — between the engine in your browser and the engine inside your spreadsheet — is the whole reason this site exists, and it is widest exactly here, at the one construct that would have solved your problem.

What does work

One thing we tested does work, and we did not expect it to:

=REGEXMATCH("naïve", "^[a-zà-ÿ]+$")   →   TRUE

An explicit range across the Latin-1 block. The engine reads à-ÿ as a range of characters, not bytes, and ï falls inside it. This is the workaround, and it is ugly, and it is what you have.

It does not cover Korean, Greek, Cyrillic, Polish ł, or Turkish ğ. For a European customer list it covers most of what you will meet.

Case folding has a limit too:

=REGEXMATCH("İSTANBUL", "(?i)istanbul")   →   FALSE

Turkish İ is a capital I with a dot, and it does not fold to a plain i. The engine is doing simple case folding, which is correct behaviour by the letter of the standard and wrong by the expectation of anyone who has a Turkish office.

What to do on Monday

Never use \w on a column of names. Not on customers, not on cities, not on product titles typed by a human. \w is a promise about ASCII wearing the word “word”.

Put one accented row in your test data. Add Müller to the sheet where you are building the formula. If the formula still works, it works. This costs one row and catches every failure on this page.

Never write [^\w] or [^A-Za-z] in a REGEXREPLACE that touches names. Those two patterns do not clean data. They delete letters, from real names, permanently, on save.

If you see #REF!, do not assume you made a typo. It may be the only honest thing Sheets has said to you all afternoon.

You can paste any of these into our tester, which now flags \w, \d, and \b as ASCII-only when it sees them, alongside the constructs Sheets cannot parse at all. The two lists are different, and the quiet one is longer.

Every formula quoted here was executed in a live Google Sheet before this was published, and the values above are the values the cells returned. The full run, with timestamps and the environment, is on the evidence page.

3

The run

Nothing above was typed from memory. Here is every formula, and the value Google returned.

Execution log

What happens to Müller, José, and 김재성 in a Google Sheets regex

Ran
Engine
Google Sheets (live, via Sheets API v4)
Locale
en_US
Host
Windows_NT 10.0.26200 · Node v24.15.0
Result
24 of 24 as expected
Cell Formula What Google returned
B1 =REGEXMATCH("Muller","^\w+$")

control: an ASCII surname is a word

TRUE
B2 =REGEXMATCH("Müller","^\w+$")

the same surname, spelled correctly

One umlaut is the entire difference.

FALSE
B3 =REGEXMATCH("Müller","^[A-Za-z]+$")

the letter-range everyone writes instead of \w

FALSE
B4 =REGEXMATCH("Smith","^[A-Za-z]+$")

control: the same range on an ASCII surname

TRUE
B5 =REGEXEXTRACT("Zürich, CH","^([A-Za-z]+)")

pull the city out of a location field

The formula that works on Zurich, Boston, and Leeds. This is the one that will be in a real spreadsheet.

Z
B6 =REGEXEXTRACT("Zürich, CH","^(\w+)")

the same extraction with \w instead

Z
B7 =REGEXEXTRACT("José Álvarez","^\w+")

first name from a full name

Jos
B8 =REGEXMATCH("김재성","^\w+$")

a Korean name

FALSE
B9 =REGEXMATCH("Müller","^\p{L}+$")

the Unicode letter class Google says is not supported

Google's wording is 'except Unicode character class matching'. This is the exact construct that phrase describes.

#REF!
B10 =REGEXMATCH("김재성","^\p{L}+$")

the Unicode letter class on a Korean name

#REF!
B11 =REGEXMATCH("MULLER","(?i)muller")

control: case-insensitive matching on ASCII

TRUE
B12 =REGEXMATCH("MÜLLER","(?i)müller")

case-insensitive matching across an umlaut

Ü and ü are the same letter to a person and to a sorting algorithm. The question is whether they are the same letter to this engine.

TRUE
B13 =REGEXMATCH("İSTANBUL","(?i)istanbul")

case-insensitive matching on a Turkish dotted capital

FALSE
B14 =REGEXMATCH("é","^.$")

does a dot match one accented character, or two of something

TRUE
B15 =REGEXMATCH("é","^..$")

or does it take two dots

FALSE
B16 =REGEXEXTRACT("Straße","^.{3}")

take the first three characters of a German word

Str
B17 =REGEXREPLACE("Müller","[^\w]","")

strip everything that is not a word character

A tidy-up formula. It is supposed to remove punctuation.

Mller
B18 =REGEXREPLACE("José","[^A-Za-z]","")

the name-cleaning formula people actually paste

Jos
B19 =REGEXREPLACE("Café ☕ 42","[^\d]","")

strip everything that is not a digit, from a line with an emoji

42
B20 =REGEXMATCH("café au lait","\bcafé\b")

a word boundary next to an accented letter

If é is not a word character, then the boundary falls in the middle of the word.

FALSE
B21 =REGEXMATCH("123","^\d+$")

are full-width digits digits

These arrive from Japanese and Korean input methods, and from Excel files exported by them.

FALSE
B22 =REGEXMATCH("١٢٣","^\d+$")

are Arabic-Indic digits digits

FALSE
B23 =REGEXMATCH("naïve","^[a-z]+$")

lowercase range against a French word

FALSE
B24 =REGEXMATCH("naïve","^[a-zà-ÿ]+$")

the accent-insensitive range people try next

A range over a block of Latin-1. Whether this is even a legal range depends on whether the engine reads the pattern as bytes or as characters.

TRUE

Every row above was written into a real Google Sheet and read back through the Sheets API on 2026-07-11. The case file and this log are in the repository, and the build refuses to run if they disagree.

Execution log

What Google Sheets regex actually supports, and what it silently does instead

Ran
Engine
Google Sheets (live, via Sheets API v4)
Locale
en_US
Host
Windows_NT 10.0.26200 · Node v24.15.0
Result
21 of 21 as expected
Cell Formula What Google returned
B1 =REGEXMATCH("abc123","\d+")

baseline: REGEXMATCH finds digits

Sanity check. If this fails, the harness itself is broken.

TRUE
B2 =REGEXEXTRACT("Order 4821 shipped","\d+")

baseline: REGEXEXTRACT pulls the first match

4821
B3 =REGEXREPLACE("John Smith","(\w+) (\w+)","$2 $1")

baseline: REGEXREPLACE swaps groups with $1 / $2

Dollar-sign backreferences are the Sheets replacement syntax.

Smith John
B4 =REGEXMATCH("abc123","abc(?=123)")

RE2 limit: positive lookahead (?=...)

Works on regex101. Here it is not #ERROR! — it is #REF!, the error people associate with a deleted cell reference. That is why they hunt for a typo that does not exist.

#REF!
B5 =REGEXMATCH("abc","a(?!b)")

RE2 limit: negative lookahead (?!...)

#REF!
B6 =REGEXMATCH("abc123","(?<=abc)123")

RE2 limit: lookbehind (?<=...)

#REF!
B7 =REGEXMATCH("abab","(ab)\1")

RE2 limit: backreference inside the pattern

Backreferences exist in the REPLACEMENT string of REGEXREPLACE, but never in the pattern.

#REF!
B8 =REGEXMATCH("héllo","\p{L}+")

Sheets limit: Unicode character class \p{L}

RE2 supports this. Google Sheets is the exception Google names in its own docs. This one catches people who already know RE2.

#REF!
B9 =REGEXMATCH("abc","(abc")

unbalanced parenthesis is also #REF!, not #ERROR!

A malformed regex and an unsupported one produce the same error. The cell tells you nothing about which.

#REF!
B10 =ERROR.TYPE(REGEXMATCH("abc123","abc(?=123)"))

cross-check: ERROR.TYPE of an unsupported pattern

Independent confirmation of the error type. ERROR.TYPE returns 4 for #REF!, 3 for #VALUE!, 7 for #N/A, 8 for #ERROR!. We do not take one measurement's word for it.

4
B11 =ERROR.TYPE(REGEXEXTRACT("abc","\d"))

cross-check: ERROR.TYPE of REGEXEXTRACT with no match

7
B12 =IFERROR(REGEXMATCH("abc123","abc(?=123)"),"caught")

an unsupported pattern is catchable with IFERROR

If IFERROR swallows it, a broken pattern can hide inside a working-looking spreadsheet forever.

caught
B13 =REGEXEXTRACT("abc","\d")

REGEXEXTRACT with no match

A different error from an invalid pattern. People wrap both in IFERROR without noticing.

#N/A
B14 =REGEXMATCH("cat dog","\bdog\b")

supported: word boundary \b

TRUE
B15 =REGEXEXTRACT("<a><b>","<(.+?)>")

supported: lazy quantifier +?

a
B16 =REGEXMATCH("ABC","(?i)abc")

supported: inline case-insensitive flag (?i)

TRUE
B17 =REGEXEXTRACT("x=1","(?P<digit>\d)")

supported: RE2 named capture group (?P<name>...)

RE2's own spelling.

1
B18 =REGEXEXTRACT("x=1","(?<digit>\d)")

supported: JavaScript named group syntax (?<name>...) also works

Measured, not assumed. Sheets accepts both spellings, and ignores the name either way — REGEXEXTRACT returns groups in order.

1
B19 =REGEXREPLACE("John Smith","(\w+) (\w+)","\2 \1")

trap: backslash backreference in the replacement string

No error. The backslashes vanish and you are left with the literal digits. Worse than an error, because an error tells you to stop.

2 1
B20 =REGEXREPLACE("abc","b","[$0]")

trap: $0 is the whole match in the replacement string

a[b]c
B21 =REGEXREPLACE("abc","b","[$1]")

trap: $1 when the pattern has no capture group

It is an error, not a silent empty string. We did not know, so we measured.

#N/A

Every row above was written into a real Google Sheet and read back through the Sheets API on 2026-07-11. The case file and this log are in the repository, and the build refuses to run if they disagree.

4