Finding ·
Google Sheets quietly deletes the umlaut from Müller
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.
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
| 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
| 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.
When a new pattern gets verified, I'll tell you
One email when a new formula is executed, logged, and published. Nothing else. Unsubscribe in one click.