Character classes, quantifiers and anchors
Build patterns out of ranges, shorthands and counted repeats, and control exactly where a match is allowed to start and end.
Classes: ranges, negation and shorthands
A character class matches exactly one character from a set. Everything inside the square brackets is a set, never a sequence, so [abc] matches one letter and never the three-character string abc.
| Class | Matches | Watch out |
|---|---|---|
[a-z] | one lowercase ASCII letter | ranges follow code-point order; [a-Z] is an error |
[^a-z] | one character outside the set | a caret negates only in the first position |
\d / \D | a digit / a non-digit | \d is ASCII-only even with the Unicode flag |
\w / \W | a word character / its negation | it is [A-Za-z0-9_]: underscore included, accented letters excluded |
\s / \S | whitespace / non-whitespace | in JavaScript the Unicode flag adds further space separators |
[.] | a literal dot | a dot is already literal inside a class; escaping it is harmless |
const code = /^[A-Z]{2}-\d{4}$/; // two letters, a dash, four digits
code.test('AB-1042'); // true
code.test('ab-1042'); // false, lowercase
code.test('AB-10422'); // false, the $ anchor rejects the extra digit
const token = /[^\s,;]+/g; // runs of non-delimiter characters
'a, b ;; c'.match(token); // [ 'a', 'b', 'c' ]
/^[\w.+-]+@[\w-]+\.[\w.]+$/.test('[email protected]'); // true[a-z] is a range while [-az] and [a\-z] are the three characters. A misplaced hyphen is the quietest bug in regex, because the pattern still compiles and still matches — just not what you meant.Quantifiers: greedy, lazy and counted
A quantifier applies to the single token on its left. A greedy quantifier takes as much as it can and then gives characters back; a lazy one takes as little as possible and then takes more. Both find a match when one exists — they differ in which match you get.
const html = '<b>one</b> and <b>two</b>';
html.match(/<b>.*<\/b>/)[0]; // '<b>one</b> and <b>two</b>' greedy
html.match(/<b>.*?<\/b>/)[0]; // '<b>one</b>' lazy
html.match(/<b>[^<]*<\/b>/)[0]; // '<b>one</b>' negated class
/<\d{2,4}>/.test('<123>'); // true, two to four digits
/<\d{2,4}>/.test('<1>'); // false
/colou?r/.test('color'); // true, the u is optional
/^ab*$/.test('a'); // true, a plus zero b*is zero or more,+is one or more,?is zero or one.ab*matches a lone a;ab+does not.{n}is exact,{n,}is at least n,{n,m}is a range. No space is allowed inside the braces, and a malformed brace is treated as a literal brace by some engines rather than as an error.- Appending
?makes a quantifier lazy. It does not make the pattern faster in general; it changes which of several valid matches is returned. - A quantifier can only repeat one token. To repeat a sequence, wrap it in a group:
(?:ab)+, notab+. - When you want everything up to a delimiter, a negated class such as
[^<]*is usually faster and clearer than a lazy dot-star.
Anchors and word boundaries
Anchors assert a position instead of consuming a character. They are the difference between matching a fragment somewhere in the subject and matching the subject itself.
/^\d{5}$/.test('12345'); // true, the whole string
/^\d{5}$/.test('12345 '); // false, a trailing space
/\d{5}/.test('12345 '); // true, no anchors, so a fragment is enough
/^\d+$/m.test('x\n12345'); // true: with m, ^ and $ also match at line breaks
/\bcat\b/.test('a cat sat'); // true
/\bcat\b/.test('concatenate'); // false, no boundary inside the word
/\bcat\b/.test('cat-like'); // true, a hyphen is a non-word character| Anchor | The position it asserts |
|---|---|
^ | start of the string, or of a line when the m flag is set |
$ | end of the string, or of a line when m is set |
\A / \z | absolute start and end — PCRE, Python and .NET, not JavaScript |
\b / \B | a word boundary, or a position that is not one |
(?<=\n) | after a newline, a portable stand-in for a multiline caret |
- An empty pattern with anchors is a valid test:
/^$/.test('')is true, which is how you detect a blank line. \bdepends on the definition of a word character, so it behaves differently next to an accented letter or a CJK character. Assert on classes you control when that matters.- Anchoring both ends is what turns a pattern into a validation rule. A pattern without anchors is a search.
FAQ
Why does my pattern match a substring of a longer word?
\b for word boundaries or ^...$ when the whole string must match. Most validation failures are missing anchors, not a wrong pattern body.Is the lazy quantifier always safer?
Related
Groups, captures and backreferences Greedy, lazy and catastrophic backtracking
Last refreshed 2026-09-18.