Regular Expressions, Explained
After reading this you will be able to read a regular expression token by token, predict what it matches, and spot the patterns that freeze your browser tab.
What a regular expression is
A regular expression (regex) is a small program that describes a set of strings. You give the engine a pattern; the engine reports whether a piece of text matches, and if so, exactly which characters matched. The Regex Tester runs your pattern against sample text as you type, highlights every match, lists the capture groups, and breaks the pattern into plain English.
Here is a hook. The pattern \b\d{3}-\d{4}\b matches a phone fragment like 555-1234. Read it left to right: a word boundary, three digits, a literal hyphen, four digits, another word boundary. That is the whole idea. A regex is a sequence of tokens, each token matching one thing, read in order.
This tester uses JavaScript regex syntax. It covers almost everything PCRE offers, with two exceptions: possessive quantifiers (a++) and recursion. For 95% of day-to-day patterns the difference never comes up.
When to reach for regex, and when not to
Regex is the right tool when the structure you want is local and roughly linear: an email-shaped token, a date, a log line prefix, a hex color. It shines at validation, search-and-replace, and pulling fields out of semi-structured text.
It is the wrong tool for nested or recursive structure. HTML and JSON are the classic traps. HTML can nest tags to any depth, and a regular language cannot count depth. If you need to read JSON, use the JSON Formatter & Validator and query it with JSONPath, or convert it with the data converter. If you find yourself writing a pattern with five levels of optional groups to parse a config file, stop and use a real parser.
A good rule: if you can describe the target in one sentence without the word "nested", regex is likely fine. The moment you say "brackets inside brackets", switch tools.
The pieces and how they combine
Every regex is built from four kinds of token, combined in order.
- Literals
- Characters that match themselves.
catmatches the three lettersc,a,tin sequence. - Character classes
- A set of allowed characters.
[a-z]matches one lowercase letter.\dis shorthand for[0-9],\wfor[A-Za-z0-9_],\sfor whitespace. - Quantifiers
- How many times the previous token repeats.
?is 0 or 1,*is 0 or more,+is 1 or more,{3}is exactly 3,{2,5}is 2 to 5. - Groups and anchors
- Parentheses
( )capture a sub-match;(?: )groups without capturing. Anchors like^(start),$(end), and\b(word boundary) match a position, not a character.
The engine matches greedily by default. A quantifier grabs as much as it can, then gives characters back only if the rest of the pattern fails. Add ? after a quantifier to make it lazy: a+? takes as little as possible. This single distinction explains most "why did it match too much?" confusion.
How matching actually runs
The JavaScript engine uses backtracking. At each choice point (a quantifier or an alternation |) it remembers where it was, tries one path, and if that path fails it returns to the remembered point and tries the next. Most of the time this is fast. The cost is bounded by the number of choice points multiplied by the input length.
Here T is roughly the number of steps the engine takes, n is the length of the input text in characters, and m is the number of independent choice points in a simple pattern. For \d{3}-\d{4} against 100 characters, expect on the order of a few hundred steps. Nothing to worry about.
The danger is when choice points nest. If a quantifier contains another quantifier that can match the same characters, the number of paths grows exponentially. This is catastrophic backtracking.
For the pattern (a+)+$ against a string of n letter a characters followed by one b, the engine explores about 2^{n} ways to split the run of a. At n = 25 that is over 33 million steps. At n = 35 it is 34 billion, and the tab freezes.
A pattern that backtracks catastrophically can freeze the tab. If your regex nests a quantifier inside a quantified group and the inner and outer parts can match the same characters, test it on short input first, then grow the input in steps.
Reading a pattern token by token
Working through the demo pattern
The demo loads the pattern (\w+)@(\w+)\.(\w+) against the text Contact [email protected] or [email protected] today. Read it in order:
(\w+)captures one or more word characters into group 1. Greedy, so it grabs as many as it can.@matches a literal at-sign.(\w+)captures the next run of word characters into group 2.\.matches a literal dot. The backslash is required, because a bare.means "any character".(\w+)captures the final run into group 3.
Against the sample the tester reports two matches:
| Match | Full text | Group 1 | Group 2 | Group 3 |
|---|---|---|---|---|
| 1 | [email protected] | alice | example | com |
| 2 | [email protected] | bob | test | org |
Notice what the pattern does not match. A real address like [email protected] would only capture b for group 1, because \w excludes the dot. This pattern is a teaching example, not a production email validator. The full email grammar in RFC 5322 is far larger than most people expect, and matching it exactly with one regex is rarely worth the effort.
Interpreting the results panel
Three outputs tell you what happened. The highlights show every match position in the text; overlapping highlights never occur, because the engine advances past each match. The capture list numbers groups from left to right by their opening parenthesis, so group 1 is always the first ( you wrote, even if it is nested inside another group. The plain-English breakdown restates each token so you can confirm the pattern says what you meant.
Two flags change everything. The global flag g makes the engine find all matches instead of stopping at the first; without it, the demo would highlight only [email protected]. The ignore-case flag i makes [a-z] also match uppercase. If your results show one match when you expected several, check whether g is on.
Common mistakes
Four errors account for most surprises.
- Unescaped metacharacters. The characters
. ^ $ * + ? ( ) [ ] { } | \have special meaning. To match a literal dot, write\.. To match a literal backslash, write\\. The String Escaper escapes regex text for you. - Greedy overreach.
".*"on the linesay "hi" and "bye"matches the whole span from the first quote to the last, capturinghi" and "bye. Use".*?"to match each quoted part. - Anchors forgotten. Validating a whole string needs
^and$. Without them,\d{4}"validates" the stringabc1234xyzbecause it matches somewhere inside. - Dot does not cross lines by default.
.matches any character except a newline. Add thesflag if you need it to span lines.
Related tools on this site
Regex often sits next to other text jobs. To clean up a query you extracted, use the SQL Formatter. To compare two structured documents instead of scanning text by eye, use the JSON Diff. To generate ignore rules rather than write path patterns by hand, use the .gitignore Generator.
Frequently asked questions
Why does my pattern match nothing when it looks right?
Check three things in order: an unescaped metacharacter (a bare . or +), a missing g flag when you expect several matches, and case sensitivity when you forgot the i flag. One of these explains most empty results.
What is the difference between a group and a capture group?
Both use parentheses. A capture group ( ) records what it matched, and appears in the result list numbered left to right. A non-capturing group (?: ) groups tokens for a quantifier without recording them. Use non-capturing groups when you only need the structure, not the value.
Why did my regex freeze the browser tab?
Almost always catastrophic backtracking. Look for a quantifier inside a quantified group where both can match the same characters, such as (a+)+ or (\w+\s*)+. Rewrite so each character has only one place it can match. Test on short input first.
Does this run on a server?
No. It runs 100% in your browser. Nothing you paste leaves your device, so you can test patterns against private log lines or sample data safely.
Is JavaScript regex the same as PCRE?
Nearly. JavaScript supports the same character classes, quantifiers, groups, lookahead, and lookbehind. It lacks possessive quantifiers (a++) and recursion. If a pattern from a PCRE tutorial fails here, one of those two features is the usual reason.