Regex character classes: \d, \w, \s, [a-z] and their traps
Character classes are the most-used part of regex and the easiest to get subtly wrong. What [abc], [a-z], [^…], \d, \w and \s really match, and the traps behind them: [A-z], the stray hyphen, the dot inside brackets, and digits that aren't 0 to 9.

· 7 min read
/^[A-Z][a-z]+$/
match
- Paris
- Lima
- Oslo
- Rome
spare
- paris
- PARIS
- New York
- R2D2
A character class matches one character from a set. [aeiou] matches one vowel. [0-9] matches one digit. \s matches one space, tab or newline.
They're in almost every pattern you'll write, and most of their traps are quiet: the pattern works on the inputs you tried and lets something odd through later. This post covers what each class really matches, then the traps. Every example is live, so edit the patterns and watch the lines change.
01A set in brackets
Square brackets list the characters allowed in one position:
greay fails because the class fills exactly one slot. To allow more than one character, put a quantifier after it: [ae]+.
02Ranges
A hyphen between two characters means "everything in between", by character code:
You can put several ranges and single characters in one class: [A-Za-z0-9_] is letters, digits and underscore.
03Negated classes
A ^ right after the opening bracket flips the set: "any character except these".
Watch that "any character except" really means any: spaces, digits, punctuation and newlines all count. 2024 matches, because digits aren't vowels.
Outside brackets, ^ is an anchor. Inside, at the very start, it's negation. Anywhere else inside it's just a caret.
04The shorthands
Some classes are so common they have short names:
| Shorthand | Means (in JavaScript) | Opposite |
|---|---|---|
\d |
a digit, [0-9] |
\D |
\w |
a word character, [A-Za-z0-9_] |
\W |
\s |
whitespace: space, tab, newline and friends | \S |
. |
any character except a line break |
Two of these surprise people. \w includes digits and the underscore, so it isn't "a letter":
And \s is more than a space: it also matches tabs and line breaks. If you mean exactly one space, write a space.
05Trap 1: [A-z] isn't "all letters"
Ranges follow character codes. Between Z (90) and a (97) sit six other characters: [ \ ] ^ _ and the backtick. So [A-z] lets them in:
Write the two ranges separately: [A-Za-z].
06Trap 2: the hyphen in the middle
A hyphen between two characters makes a range, even when you meant a literal hyphen:
That comma is the bug: + is code 43, . is 46, and the comma (44) sits between them. Put the hyphen first or last in the class, or escape it, and it's a plain hyphen again:
07Trap 3: the dot and other specials
Most special characters lose their powers inside brackets. [.] is a literal dot, [*+?] is one of three symbols, and [$] is a dollar sign. So [.] is another way to write \.:
Only four characters stay special inside a class: ], \, ^ at the start, and - between two characters.
08Trap 4: digits that aren't 0 to 9
In JavaScript, \d is exactly [0-9]. In Python 3, on a normal str pattern, \d matches any Unicode digit: Arabic-Indic ٣, Devanagari ३, full-width 3. The same goes for \w, which accepts letters from any script.
import re
re.fullmatch(r"\d+", "٣٤٥") # matches in Python
re.fullmatch(r"\d+", "٣٤٥", re.ASCII) # None
If a number is about to go into int() or a database column, that matters. Use [0-9], or the re.ASCII flag, when you mean ASCII digits.
09Try one
Can you catch the lines made only of hex digits?
Catch strings made only of hex digits, in either case.
Targets · 0/6 caught
- fftarget
- 00target
- 1a2Btarget
- DEADbeeftarget
- c0ffeetarget
- 7target
Innocents · 0 hit
- fginnocent
- 0x1finnocent
- 12 34innocent
- beef!innocent
- gginnocent
- zinnocent
Type a regex — the board updates as you go.
10Checklist
[abc]matches one character from the set. Add a quantifier for more.[^abc]matches any character not in the set, including spaces and newlines.- Write
[A-Za-z], never[A-z]. - Put a literal hyphen first or last:
[\w.-]. - Inside brackets,
.*+?$are literal. \wincludes digits and_.\sincludes tabs and newlines.- In Python,
\dand\ware Unicode-aware. Use[0-9]orre.ASCIIfor ASCII only.
11Keep practising
- Character castles and Shorthand patrol are practice packs that drill exactly this.
- The guide has a full section on classes and shorthands.