How to Test Regular Expressions: Patterns, Flags and Capture Groups Explained

A regular expression is a tiny pattern language for finding text: a few characters that describe “an email address,” “a US phone number,” or “every word starting with a capital letter,” and an engine that finds every match. The trouble is that a pattern that looks right can silently match the wrong things — or nothing at all — and the only cure is testing it against real sample text. Our online regex tester is built for exactly that loop: type a pattern into the pattern box, paste sample text below it, press Test Regex, and matches highlight instantly with a count and capture-group details for every hit. Nothing leaves your browser — the pattern compiles and runs with the built-in JavaScript regex engine — and you do not need slashes or delimiters around the pattern. This guide walks through the pieces of a pattern, the flags that change its behaviour, a worked email example, and the mistakes that cost developers the most time.

How to Use the Tester

1. Type the pattern. Enter it bare, without surrounding slashes: \d{3}-\d{4}, not /\d{3}-\d{4}/. 2. Paste sample text. Include both text that should match and text that should not — a tester fed only matching examples cannot reveal over-matching, which is the more expensive failure. 3. Set the flags. The flags are toggles: g for global, i for case-insensitive, m for multiline, s for dotAll, u for Unicode, and y for sticky (explained below). 4. Read the match details. Beyond the highlights, the tester lists each match with its capture groups, so you can see exactly what each parenthesised part captured before the pattern ever reaches your code. If you are short of a starting point, the Email, URL, and US Phone preset buttons drop in a working pattern for each case — a far better start than a blank box. When a pattern is meant to validate data you will later encode or store, our Base64 encoder handles the text-safe side of that pipeline, and our JSON formatting guide covers the structured data the matches usually end up in.

The Anatomy of a Pattern

Most patterns are built from five kinds of pieces. Literals match themselves: cat matches the letters c-a-t. Character classes match one character from a set: [A-Z] is any capital letter, \d is any digit, \w is a word character (letters, digits, underscore), and \s is whitespace. Quantifiers say how many: * means zero or more, + means one or more, ? means zero or one, and {3} or {2,4} give exact counts — so \d{5} is exactly five digits, which is why it will never match a four-digit number. Anchors say where: ^ is the start and $ is the end (of the text, or of each line with the m flag). Groups, in parentheses, do two jobs at once: they group pieces so a quantifier applies to all of them, and they capture what matched, so (\d{3})-(\d{4}) captures the area code as group 1 and the rest as group 2. Alternation with | gives choices: cat|dog matches either word. Everything else in regex is a variation on these five ideas, which is why a pattern that looks intimidating is usually just a long chain of them.

Flags: The Six Switches That Change Everything

Flags are the most common reason a correct-looking pattern behaves unexpectedly, so learn them as a set. The g flag (global) finds all matches instead of stopping at the first — without it, a tester shows you one match and hides the rest. The i flag makes matching case-insensitive, so “Email” matches “email”; without i, the same pattern misses half your data. The m flag (multiline) lets ^ and $ match at each line boundary instead of only at the very start and end of the whole text, which matters the moment your sample has more than one line. The s flag (dotAll) lets the dot match line breaks too, so .* can span lines. The u flag enables Unicode mode, which makes the engine handle emoji and astral-plane characters correctly instead of treating them as two half-characters. The y flag (sticky) anchors each match attempt to the engine's current position, a specialist tool for parsers rather than everyday searching. When a pattern finds nothing, check the flags first: a missing i or a stray ^ without m explains most empty results before any deeper debugging is needed.

Worked Example: Matching an Email Address

Take the pattern from the tester's own Email preset, in spirit: ([A-Za-z0-9._%+-]+)@([A-Za-z0-9.-]+\.[A-Za-z]{2,}). Read it left to right. Group 1, [A-Za-z0-9._%+-]+, is one or more characters from the username set — letters, digits, dot, underscore, percent, plus, hyphen. Then a literal @. Then group 2, the domain: [A-Za-z0-9.-]+, one or more domain characters, then an escaped dot \. (a bare dot would match anything, so it must be escaped to mean a literal dot), then [A-Za-z]{2,}, two or more letters for the top-level domain. Paste “Contact ada@example.com or bob@test.org for help” and you get two matches; the match details show group 1 as “ada” and “bob” and group 2 as “example.com” and “test.org” — exactly the pieces code would pull out as match[1] and match[2]. Paste “ada@example” and it does not match, because the top-level domain needs at least two letters after the dot. That is the pattern doing its job: a validator that accepts “ada@example” would pass bad data into your system, and the tester shows you the rejection before your users do.

Common Mistakes

Forgetting to escape literal special characters. A bare dot matches anything, a bare parenthesis breaks the pattern, and a bare plus quantifies whatever precedes it. If you mean the literal character, escape it: \., \(, \+. Anchoring too tightly or not at all. ^\d+$ requires the whole text to be digits; without anchors, the same pattern matches a digit substring inside anything. Greedy quantifiers eating too much. .* grabs as much as it can; when it swallows the closing tag you wanted, try the lazy form .*?, which grabs as little as possible. Testing only matching examples. Feed the tester near-misses too — the wrong case, the short number, the missing dot — because a pattern's failures are where its real behaviour lives. Quantifier with nothing before it. A pattern starting with * or + is invalid; the tester shows the engine's exact error plus a plain-language hint pointing at the likely culprit, so fix that spot and re-test rather than rewriting the whole pattern. Assuming one engine fits all. This tester uses the JavaScript engine, so a pattern tuned for another language's engine (lookbehind support and flag details differ) should be re-tested here before it ships in browser code.

Limits Worth Knowing

The tester runs entirely in your browser, so your sample text is never uploaded — but that also means it tests JavaScript regex semantics, not Python's or Go's. Very long texts and pathological patterns (nested quantifiers such as (a+)+) can run slowly, because backtracking grows fast; if a test hangs, simplify the pattern rather than feeding it more text. And a tester can only show you what a pattern does, not what you meant: the final check is always a human reading the match list and asking, “is that really every case I want, and only those cases?”

Frequently Asked Questions

How do I test a regular expression online?
Type your pattern into the pattern box and paste sample text below it — matches highlight instantly, with a count and capture-group details for every hit. You do not need slashes or delimiters around the pattern, and flags like g (global) and i (case-insensitive) are toggles. Use the Email, URL, or US Phone presets if you want a working example to start from.
What do the regex flags g, i, m, s, u and y do?
g (global) finds all matches instead of stopping at the first; i makes matching case-insensitive; m (multiline) lets ^ and $ match at each line boundary instead of only the start/end of the text; s (dotAll) lets . match line breaks; u enables Unicode mode for correct emoji and astral-character handling; y (sticky) anchors each match attempt to the engine's current position.
Why is my regex invalid, and how do I fix it?
The most common causes are unbalanced parentheses or brackets, a quantifier like * or + with nothing before it, or a special character that needs escaping (for example, a literal dot should be \. and a literal parenthesis should be \(). This tester shows the engine's exact error plus a plain-language hint pointing at the likely culprit — fix that spot and re-test.
What are capture groups in regex?
Anything wrapped in parentheses becomes a capture group, and the engine remembers what that part matched. In ([A-Za-z0-9._%+-]+)@([A-Za-z0-9.-]+\.[A-Za-z]{2,}), group 1 holds the username and group 2 holds the domain. The match-details list in this tester shows each group's contents per match, which is exactly how you would access them in code (match[1], match[2]).
Why does my pattern find no matches?
First check the flags: without i, "Email" will not match "email". Then check your anchors — ^ and $ require the match to span the whole text (or whole line with m). Then look at quantifiers: \d{5} will not match a 4-digit number. Finally, confirm you escaped literal special characters: an unescaped . matches anything, which usually over-matches rather than under-matches, but an unescaped ( breaks the pattern entirely.

More guides from ToolNest

How to Format JSON: Validation, Indentation and the Errors That Break Parsers

Paste, validate, pretty-print: the formatter that pinpoints the exact error, counts keys and depth, and turns a one-line blob into readable, diffable JSON.

How to Encode and Decode Base64: What It Is, What It Is Not, and When to Use It

Three bytes become four characters, output grows by about a third, and the = padding marks where the data ends — plus the one rule that matters: Base64 is not encryption.

Cron Expression Generator

Build a portable 5-field schedule with plain-language previews and the next run times — the companion tool to this batch's cron guide.