g finds every match; i ignores case; m spans lines.
Try one
JavaScript regular expression
/([\w.-]+)@([\w.-]+)/g · 1 matches
39 chars
Match preview
Ping jane.doe@example.com on 2025-11-02
#1
jane.doe@example.com
$1=jane.doe · $2=example.com
Pattern reading
Capture group start · Character class start · Word character · Character class end · One or more · Capture group end · Capture group start · Character class start · Word character · Character class end · One or more · Capture group end
Write a regular expression against real text and see every match highlighted as you type, with the capture groups broken out. Switch to replace mode to check what $1 actually produces, and copy the pattern out as JavaScript, TypeScript, or Python so the escaping is right for the language you are pasting into.
Modes: match and replace
2
Languages for export
3
Groups broken out per match
live
Characters uploaded
0
Step by step
How to use it
01Write the pattern and set the flags
g for every match rather than the first, i for case-insensitive, m to make ^ and $ match line boundaries, s to let . cross newlines. Most surprises in a regex are flag surprises.
02Paste text that includes the awkward cases
Test against the strings that nearly match, not just the ones that should. A pattern is only as good as the false positives you tried to give it.
03Read the capture groups
Each match is broken out by group, so you can see whether group 1 is what you think it is before you write code that depends on it.
04Check the replacement, then export
Replace mode shows the actual result of your $1 references. Export renders the pattern with the escaping the target language needs — which is where hand-copying usually goes wrong.
Worked example
Greedy against lazy
The single most common regex bug, in one comparison. Both patterns are correct; they answer different questions, and only one of them is usually the question you asked.
Given
Input
<b>bold</b> and <i>italic</i>
Goal
each tag separately
Difference
one character
<.+> against <.+?>
<.+> 1 match
<b>bold</b> and <i>italic</i>
. matches > too, so it runs to the
LAST > in the string
<.+?> 4 matches
<b> </b> <i> </i>
the ? makes it stop at the first >
<[^>]+> 4 matches, and faster
says what it means: not a >
Greedy
1 match
Lazy
4 matches
Best
a negated class
Quantifiers take as much as they can and give back only when the rest of the pattern fails, so .+ swallows the whole line before backtracking. Adding ? makes it take as little as possible. The better fix is usually neither: a negated character class states the boundary directly, which is clearer to read and avoids the backtracking that makes pathological patterns slow.
Before you ship the pattern
What to know about regular expressions
Flavours differ in the details that matter
Lookbehind, named groups, and Unicode property escapes are not available everywhere, and the same pattern can behave differently in JavaScript, Python, PCRE, and Go. Test in the flavour you will run in — exporting for a language is not the same as guaranteeing identical semantics.
Catastrophic backtracking is a real outage
Nested quantifiers over overlapping alternatives — the classic (a+)+ shape — can take exponential time on an input that merely nearly matches. On a server handling user input that is a denial of service, and it has taken down large sites. Prefer specific character classes to nested quantifiers.
The g flag makes the regex stateful in JavaScript
A global regex object carries lastIndex between calls, so reusing one across test() calls returns alternating results. Either create the regex where you use it, or reset lastIndex deliberately — this is the bug that looks like the pattern working every other time.
Anchors mean different things with m
Without the multiline flag ^ and $ match the start and end of the whole string; with it they match every line boundary. A pattern that works on a single line and fails on a file is usually missing m — or has it when it should not.
Some things are not regular
Nested structures — HTML, JSON, balanced brackets — cannot be matched reliably by a regular expression, no matter how clever it gets. Use a parser. A regex is the right tool for finding a shape inside text, not for understanding a tree.
Everything runs in your browser
The pattern is executed locally against your text, so testing against production log lines or customer records is safe. Nothing is uploaded, and the share link carries the pattern and settings.
The judgement call
Is a regex the right tool?
Regular expressions are excellent at one job and routinely used for three others.
Finding a shape inside text
Yes
Emails, IDs, timestamps, log prefixes. This is precisely what the tool is for.
Validating a format loosely
Yes, loosely
Good for catching typos in a form field. Strict validation of real-world formats is another matter.
Extracting parts of a match
Capture groups
One pass gives you the whole match and its pieces — check the group breakdown before relying on the index.
Parsing HTML or XML
Use a parser
Nesting is not regular. Any pattern that appears to work will fail on the first unusual document.
Fully validating an email address
Send a mail
The RFC-compliant pattern is thousands of characters and still proves nothing about deliverability.
A pattern with nested quantifiers on user input
Rewrite it
That shape is where catastrophic backtracking lives. A negated character class is usually the fix.
Reference
The flags and what they change
g — global
Every match, not just the firstIn JavaScript it also makes the regex object stateful via lastIndex, which surprises people regularly.
i — ignore case
Case-insensitive matchingApplies to character classes too, so [a-z] with i also matches A–Z.
m — multiline
^ and $ match line boundariesWithout it they only match the start and end of the entire string.
s — dotAll
. matches newlinesOff by default, which is why a pattern spanning lines silently returns nothing.
Greedy vs lazy
+ against +?Greedy takes as much as possible and backtracks; lazy takes as little as possible and extends.
Groups
( ) capture · (?: ) do notNon-capturing groups keep the group numbering clean and skip the work of storing a result.
FAQ
Questions, answered plainly
How do I test a regular expression?
Write the pattern, set the flags, and paste text that includes both the strings that should match and the ones that nearly do. Every match is highlighted as you type and broken out by capture group, so you can confirm what group 1 actually contains before writing code around it.
What is the difference between greedy and lazy quantifiers?
Greedy quantifiers like .+ take as much as they can and give characters back only when the rest of the pattern fails; lazy ones like .+? take as little as possible and extend. It is why <.+> matches an entire line of HTML in one go while <.+?> matches each tag separately.
Why does my pattern not match across lines?
Two different flags. The dot does not match a newline unless you set s, and ^ and $ only match the string boundaries unless you set m. A pattern that works on one line and fails on a file is nearly always missing one of the two.
What is catastrophic backtracking?
A pattern whose nested quantifiers can be satisfied in exponentially many ways, so an input that almost matches takes effectively forever to reject. The classic shape is a quantifier inside a quantifier over overlapping alternatives. On a server processing user input it is a denial-of-service vulnerability, not just slowness.
Can I parse HTML with a regular expression?
No, and it is the most famous answer in the subject for good reason. HTML nests arbitrarily and regular expressions cannot track nesting. A pattern that works on your sample document will fail on the first one with an unexpected attribute, comment, or nested tag.
Will my pattern work the same in Python or Go?
Mostly, but not always. Lookbehind, named group syntax, and Unicode property escapes differ between flavours, and Go's RE2 deliberately omits backreferences and lookaround entirely. Export gets the escaping right for the target language; the semantics still need testing where you will run it.
The pattern runs against your text in your browser. Nothing is uploaded, so testing against production log lines is safe.