A little help goes a long way. Find a tool/

Developer FIELD NOTES · 5 MIN READ

How Regular Expressions Work

The regex vocabulary — character classes, quantifiers, anchors, groups — and three everyday patterns explained token by token.

A regular expression is a tiny pattern language that a regex engine reads character by character, matching text wherever the pattern fits. Regex has a reputation for looking like line noise, but every symbol has one fixed meaning — learn about thirty of them and you can read almost any pattern ever written.

The tokens: literals, escapes, and character classes

Most characters in a pattern match themselves: cat matches "cat". Special characters — . ^ $ * + ? ( ) [ ] { } | \ — mean something else, and a backslash escapes them back to their literal selves: \. matches an actual dot. Shorthand escapes stand in for common classes: \d is any digit 0-9, \w is a "word" character (letters, digits, underscore), \s is any whitespace (spaces, tabs, newlines), and the uppercase versions (\D, \W, \S) mean "anything but".

Square brackets define your own class, matching any one character inside: [aeiou] is a vowel, [a-z] any lowercase letter, [0-9a-f] any hex digit. A leading ^ inside the brackets flips it — [^0-9] matches any character that is not a digit. Note the two lives of ^: inside brackets it negates, outside brackets (as we will see) it anchors. Finally, . matches any single character except a newline, which is why escaping it (\.) matters so much in patterns that touch file names and domains.

Quantifiers and greediness

A quantifier says how many times the previous item may repeat: * is zero or more, + is one or more, ? is zero or one, and {2,4} is between two and four ({3} exactly three, {2,} two or more). By default quantifiers are greedy: they grab as much as possible. On the text <em>hi</em>, the pattern <.+> matches the entire string, because .+ swallows everything up to the last >. Adding a question mark makes the quantifier lazy — <.+?> stops at the first > and matches only <em>. Greedy vs lazy is the single most common reason a "correct-looking" regex matches too much.

Anchors, groups, and alternation

Anchors match positions, not characters: ^ is the start of the string, $ the end, and \b a word boundary (the invisible edge between a word character and anything else — \bcat\b matches "cat" but not "category"). Parentheses create capturing groups, which both group items for a quantifier (as in (ab)+) and extract the matched text for later use. (?:...) groups without capturing, and (?<year>...) names the group. The vertical bar | is alternation — "or" — so cat|dog matches either word.

Practical pattern 1: validating a date shape

^\d{4}-\d{2}-\d{2}$

Reading left to right: ^ — the match must start at the very beginning of the string; \d — a digit; {4} — exactly four of them (the year); - — a literal hyphen; \d{2} — exactly two digits (the month); another literal -; \d{2} — two more (the day); $ — and the string must end there. The anchors are what turn this into validation: without them, the pattern would happily match "1234-56-78" hiding inside longer junk. It checks the shape — 2024-13-45 would pass, since real calendar logic is a separate job — but it rejects anything not formatted as YYYY-MM-DD.

Practical pattern 2: pulling an email into parts

^[\w.+-]+@[\w-]+\.[\w.]+$

Breaking it down: ^ start; [\w.+-]+ — one or more characters from the class "word characters, dots, plus, hyphen" (the local part, so first.last+news fits); @ — the literal at-sign; [\w-]+ — the domain's first label (letters, digits, hyphens); \. — a real dot (escaped, not "any character"); [\w.]+ — the rest of the domain, dots included; $ end. Capture groups could wrap each side — ([\w.+-]+)@([\w.-]+) — to extract username and domain separately. This pattern is a pragmatic heuristic, not full RFC 5322 compliance: it accepts things like [email protected] and misses exotic legal addresses. For form validation, "matches the common shapes, rejects the obvious garbage" is usually exactly the trade-off you want.

Practical pattern 3: cleaning up whitespace

\s+        → replace with " "   (collapse runs of whitespace to one space)
^\s+|\s+$  → replace with ""    (trim leading and trailing whitespace)

The first pattern is one class plus a quantifier: \s any whitespace, + one or more of them, replaced by a single space — user-pasted text with double spaces or stray tabs becomes tidy in one pass. The second uses alternation: ^\s+ is whitespace at the start, \s+$ is whitespace at the end, and replacing either with nothing trims the string. These two patterns power a surprising amount of data cleaning, and the same splitting logic — cut on \s+ — is how tools count words in text.

Try it yourself

Type any of the patterns above into the Regex Tester with your own sample text — it highlights every match live and shows the capture groups and positions, with plain error messages when a pattern is invalid. When you meet a regex you did not write, paste it into the Regex Explainer, which breaks it into tokens and explains each one in plain English — the fastest way to audit someone else's pattern. And to see the whitespace-splitting idea from pattern 3 in action, the Word Counter counts the words that \s+ splitting produces in your text.

Frequently asked questions

What do \d, \w, and \s mean in regex?
They are shorthand character classes: \d is any digit 0-9, \w is a word character (letters, digits, underscore), and \s is any whitespace. The uppercase versions — \D, \W, \S — mean 'anything except' that class.
What is the difference between * and + in regex?
Both are quantifiers: * means zero or more of the previous item, + means one or more, ? means zero or one, and {2,4} means between two and four. So \d+ matches one or more digits while \d* also matches an empty string — the difference matters when a pattern must not match nothing.
Why does my regex match too much text?
Almost always a greedy quantifier. *, +, and {m,n} grab as much as possible, so on '<em>hi</em>' the pattern <.+> matches the whole string. Add a question mark to make it lazy — <.+?> stops at the first > and matches only '<em>'. Greedy versus lazy is the most common cause of over-matching.
What do ^ and $ mean in regex?
Outside brackets they are anchors: ^ matches the start of the string and $ the end, turning a search pattern into full-string validation (as in ^\d{4}-\d{2}-\d{2}$ for a YYYY-MM-DD date). Inside square brackets ^ negates the class instead, and \b marks a word boundary.
What do parentheses do in a regex?
They group and capture. Grouping lets a quantifier apply to a whole unit, as in (ab)+; capturing saves the matched text so you can extract, for example, the username and domain from (\w+)@(\w+)\.com. Use (?:...) when you need grouping without saving the capture.