Regex word boundaries explained
The \b anchor matches a position, not a character. It sits at the boundary between a word character and a non-word character, which is what makes \bcat\b match cat in a cat sat but not inside category. Understanding exactly where that position falls, and where its definition breaks down, saves a lot of confusion.
What counts as a word character
A boundary is defined entirely by the \w class, which in the ASCII sense is [A-Za-z0-9_]. A boundary exists at any point where one side is a \w character and the other side is not, including the start and end of the string if the adjacent character is a word character.
So in the string (cat), boundaries exist between ( and c, and between t and ). The pattern \bcat\b matches because the parentheses are non-word characters sitting next to word characters.
| Position | Left side | Right side | Boundary? |
|---|---|---|---|
start of cat | nothing | c | yes |
inside category after cat | t | e | no |
cat. between t and . | t | . | yes |
_cat between _ and c | _ | c | no |
That last row trips people up: underscore is a word character, so there is no boundary between _ and c. \bcat will not match the cat in _cat.
The B anchor
\B is the exact negation: it matches any position that is not a word boundary. \Bcat\B matches cat only when it is surrounded by other word characters, as in scathing. It is less common but useful for finding a substring embedded inside a larger word.
Why boundaries break on accented text
Here is the gotcha that causes real bugs. In most engines the default \w is ASCII only, so accented and non-Latin letters are treated as non-word characters. That means \b sees a boundary in the middle of a word.
Take the name café. The é is not in [A-Za-z0-9_], so there is a boundary between caf and é. The pattern \bcafé\b may fail to match as a whole word in the way you expect, and \w+ will stop at caf. The same happens with naïve, Zürich, Cyrillic, Greek, or Han characters. Your pattern silently splits words at every accented letter.
Practical fixes
The fix depends on your engine.
- JavaScript: there is no Unicode-aware
\b. Work around it by matching explicit letter ranges with theuflag, or use lookarounds against\p{L}, for example(?<!\p{L})café(?!\p{L})with theuflag. The Unicode property\p{L}matches any letter in any script. - PCRE, PHP, Java: pass the Unicode flag so
\wand\bbecome Unicode aware. In PCRE this is(*UCP)or theumodifier; in Java usePattern.UNICODE_CHARACTER_CLASS. - .NET:
\wis Unicode aware by default, so\bgenerally does the right thing with accented text without extra flags. - Python
re: strings are Unicode by default in Python 3, so\walready includes accented letters unless you passre.ASCII.
The portable approach, when you cannot rely on Unicode-aware boundaries, is to replace \b with explicit lookarounds that test for the absence of a letter on each side. It is more verbose but it matches your actual intent: a word edge, defined by whatever your language considers a letter.
To see exactly where a boundary falls, paste accented and plain text side by side and watch the highlights shift. The Regex Tester makes these invisible positions visible.