Character Classes
A character class matches one character from a set. [abc] matches a, b, or c; [a-z] any lowercase letter; [^...] negates. Built-in shortcuts like \d, \w, \s, plus Unicode property escapes, cover almost everything you need.
Sets, ranges, negation, Unicode
EXAMPLE
// 1) Basic sets
/[aeiou]/.test('hello') // true — any vowel
/[aeiou]/g.exec('hello') // ['e'] — first vowel
'aeiou'.match(/[xyz]/) // null
// 2) Ranges
/[a-z]/.test('Hello') // true — 'e' is lowercase
/[A-Z]/.test('hello') // false
/[a-zA-Z]/.test('Hello') // true
/[0-9]/.test('order42') // true
/[a-fA-F0-9]/.test('1c') // true — hex digit
// 3) Negated set
/[^aeiou]/.test('aeiou') // false
/[^0-9]/.test('123abc') // true — 'a'
/[^aeiou ]/g.exec('hello world') // matches consonants and the space
// 4) Escaping inside character classes
// ] must be escaped: /[\]]/
// \ must be escaped: /[\\]/
// - only special between two chars; put first or last to use literally: /[-a-z]/, /[a-z-]/
// ^ only special as the FIRST char (negation); literal elsewhere: /[a^b]/
// 5) Built-in shortcuts
// \d digit equivalent to [0-9]
// \D non-digit [^0-9]
// \w word [A-Za-z0-9_]
// \W non-word
// \s whitespace space, tab, newline, etc.
// \S non-whitespace
// . any except \n (without dotall flag)
/^\d{3,4}-\d{4}$/.test('555-1234') // true — phone-like
/[\w.+-]+@[\w-]+\.\w+/.test('a@b.co') // true — simple email shape
// 6) Unicode property escapes (with /u flag — JS, modern engines)
/^\p{L}+$/u.test('Привет') // true — any letter
/^\p{N}+$/u.test('一二三') // true — any numeric character
/\p{Emoji}/u.test('hi 👋') // true
/\p{Script=Greek}/u.test('αβγ') // true
/\P{Cn}/u // anything assigned (negate 'unassigned')
// 7) ASCII vs Unicode \w
// Default \w in JS is [A-Za-z0-9_] — no accents, no emoji, no CJK.
/\w+/u.test('café') // matches 'caf' then 'café' splits — wait, depends on engine
// To match Unicode letters, use \p{L} instead.
/^[\p{L}\p{N}_]+$/u.test('café_42') // true
// 8) Combining sets — union and intersection
// Union — easy, just list both ranges
/[A-Za-z0-9]/
// Intersection — needs the v flag (modern engines) or workaround
/[\p{Letter}&&\p{ASCII}]/v.test('é') // false — letter intersected with ASCII
// Without the v flag, write the explicit range you want.
// 9) Common patterns built from character classes
// Hex color /^#[0-9a-fA-F]{3,8}$/
// Slug /^[a-z0-9]+(?:-[a-z0-9]+)*$/
// UUID /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
// IPv4 octet /^25[0-5]|2[0-4][0-9]|1?[0-9]{1,2}$/ (one octet, used 4x)
// Base64 /^[A-Za-z0-9+/=]+$/
// 10) POSIX classes (some engines — not JS by default)
// [:alpha:] letters
// [:digit:] digits
// [:upper:] upper-case
// [:space:] whitespace
// [:punct:] punctuation
// Used as /[[:alpha:]]/ — JS doesn't support these natively.
// 11) Whitespace gotchas
// \s matches \u00a0 (non-breaking space), tabs, line separators
// In some engines \s includes Unicode whitespace; in others ASCII only
// Use [\s\u200B-\u200D] to also catch zero-width spaces when scrubbing
// 12) Newlines
// . matches any except \n by default
// Use the s flag (dotall): /^foo.bar$/s.test('foo\nbar') → true
// 13) Real-world examples
// Detect simple credit-card patterns (use a library for real validation; Luhn check required)
/^(?:\d[ -]?){13,19}$/.test('4111 1111 1111 1111')
// Match a single Unicode word boundary correctly (best-effort)
/^\p{L}[\p{L}\p{N}\p{Mn}_'-]*$/u.test("O'Hara-Smith")
// Strip non-printable control characters before storing
function sanitize(s) {
return s.replace(/\p{C}/gu, ''); // \p{C} = control + format + private use + unassigned + surrogate
}
// 14) Performance tips
// • A class with many alternatives is faster than | alternation: prefer [abc] over (a|b|c)
// • Avoid nested quantifiers + negated classes (catastrophic backtracking risk)
// • Anchor patterns (^, $, \b) to give the engine a fixed starting position
// • Compile once if your runtime allows it (don't rebuild per call inside a loop)
// 15) Common bugs
// • [0-9] inside an Unicode-only ranged set — accidentally matches digits from other scripts when /u is set
// • Forgetting to escape ] inside a class on engines that require it
// • Putting - in the middle of a class without intending a range: [a-z] is a range, [-az] / [az-] are literals
// • Using \d expecting only ASCII — with /u, \d still ASCII; \p{Nd} is the Unicode equivalent
// • Negating with [^] expecting an empty class — that matches ANY character (most engines)
// • Building dynamic regex with unsanitised input — escape with /[.*+?^=!:{}()|\\[\\]\\/\\\\]/g.replace(...)
Why it matters
Pick character classes over alternations — [abc] is one decision per char, (a|b|c) is three. Use \d, \w, \s for ASCII-only fields, switch to \p{L}, \p{N}, \p{Emoji} with the /u flag whenever real human text shows up, and remember that JS’s default \w doesn’t match accented letters.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
[abc] // a, b, or c [^abc] // anything except a, b, c [a-z0-9] // a–z OR 0–9 \d \D // digit / non-digit \w \W // word char / non-word \s \S // whitespace / non-whitespaceTry it Yourself »
Exercise
Escape for any digit 0-9.
/
+/
Backslash + d.
Discussion
Loading…