What is a regular expression?
A regular expression (regex, regexp, or RE) is a sequence of characters that defines a search pattern. Originally formalized by mathematician Stephen Kleene in the 1950s as a way to describe regular languages, regular expressions became a programmer's tool in the 1970s when Ken Thompson built them into the ed editor and later grep. Today every modern programming language ships a regex engine — JavaScript's RegExp, Python's re, PCRE in PHP, Java's java.util.regex, Go's regexp, and Rust's regex crate.
Regex shines for tasks that look easy in English but are awkward in plain code: "find every email address in this log", "extract the version number from a release tag", "validate that a phone number has 10 digits", "split a CSV row that may contain quoted commas." A pattern of 10–30 characters can replace 50 lines of imperative parsing logic. The trade-off is readability — regex is famously write-only. The pattern ^(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"...")@ is a real fragment from email validation.
Modern engines support far more than the 1950s formalism. Lookahead, lookbehind, named groups, backreferences, unicode property escapes, possessive quantifiers — each addition makes patterns more powerful but also riskier. The most-cited regex bug in production, catastrophic backtracking, can hang an entire process for seconds on a 100-character input.
How regex matching works under the hood
Regex engines come in two architectures, and the choice affects what your pattern can do and how fast it runs:
| Engine type | Used by | Pros | Cons |
|---|---|---|---|
| Backtracking (NFA) | JavaScript, Perl, Python re, PCRE, Java, Ruby, .NET |
Supports lookaround, backreferences, recursion. Most "modern features." | Worst-case exponential time. Catastrophic backtracking on adversarial input. |
| Linear-time (DFA / hybrid) | Go's regexp, Rust's regex, RE2, awk, grep, lex |
Linear O(n) time. Predictable performance. Cannot hang. | No backreferences, no lookaround (Go & Rust). Fewer features. |
The matching process for a backtracking engine, conceptually:
- Compile the pattern into a state machine (NFA). Most languages cache compiled regex; recompiling in a hot loop is wasteful.
- Walk the input character by character, advancing through states. When a quantifier like
*is met, the engine greedily consumes as many characters as possible. - On mismatch, the engine backtracks — gives up matched characters one at a time and tries alternative paths. This is where pathological patterns hang: a pattern like
(a+)+bon inputaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacreates 2^n backtracking paths. - Report match position and capture groups, or fail.
Regex flavor comparison — JavaScript vs PCRE vs Python
"Regex" is not one language. Each ecosystem has subtle differences. Patterns that work in PHP may fail or behave differently in JavaScript. Knowing the deltas saves hours of debugging.
| Feature | JavaScript | Python (re) | PCRE (PHP, Perl) | Go / Rust (RE2) |
|---|---|---|---|---|
Lookahead (?=...) |
Yes | Yes | Yes | No |
Lookbehind (?<=...) |
Yes (ES2018+) | Yes (fixed-width only) | Yes (variable-width) | No |
Named groups (?<name>...) |
Yes (ES2018+) | Yes ((?P<name>...)) |
Yes | Yes (Rust) |
Backreferences \1 |
Yes | Yes | Yes | No |
Unicode property \p{L} |
Yes (with u flag) |
Yes (Python 3) | Yes | Yes |
Possessive quantifier *+ |
No | No (use atomic groups) | Yes | No (not needed) |
| Catastrophic backtracking risk | High | High | High | None — guaranteed linear time |
This tester uses the JavaScript flavor via the browser's native RegExp object. Patterns tested here will work identically in Node.js and most modern browser-based code. For PCRE-specific features, test in PHP's preg_match; for safety in untrusted input, switch to RE2 / Go.
Pattern anatomy — a regex cheat sheet
Anchors and boundaries
| Token | Meaning | Example match |
|---|---|---|
^ | Start of string (or line, with m flag) | ^foo matches "foo" only at start |
$ | End of string (or line, with m flag) | bar$ matches "bar" only at end |
\b | Word boundary | \bcat\b matches "cat" but not "category" |
\B | Non-word boundary | \Bcat matches "cat" inside "concatenate" |
Character classes
| Token | Meaning | Equivalent |
|---|---|---|
. | Any character (except newline, unless s flag) | [^\n] |
\d | Digit | [0-9] |
\D | Non-digit | [^0-9] |
\w | Word character | [A-Za-z0-9_] |
\W | Non-word character | [^A-Za-z0-9_] |
\s | Whitespace | [ \t\n\r\f\v] |
[abc] | Custom class — a, b, or c | — |
[^abc] | Negated class — anything but a, b, c | — |
[a-z] | Range | — |
Quantifiers
| Token | Meaning | Greedy / lazy |
|---|---|---|
* | 0 or more | Greedy. Use *? for lazy. |
+ | 1 or more | Greedy. Use +? for lazy. |
? | 0 or 1 (optional) | Greedy. Use ?? for lazy. |
{n} | Exactly n | — |
{n,} | n or more | Greedy. Use {n,}? for lazy. |
{n,m} | Between n and m | Greedy. Use {n,m}? for lazy. |
Groups and lookaround
| Token | Meaning |
|---|---|
(...) | Capture group — captures match for backreferences |
(?:...) | Non-capturing group — groups without storing |
(?<name>...) | Named capture group (ES2018+) |
(?=...) | Positive lookahead — match if followed by |
(?!...) | Negative lookahead — match if NOT followed by |
(?<=...) | Positive lookbehind — match if preceded by |
(?<!...) | Negative lookbehind — match if NOT preceded by |
\1, \2... | Backreference to numbered capture group |
Flags
| Flag | Effect |
|---|---|
g | Global — find all matches, not just first |
i | Case-insensitive |
m | Multiline — ^ and $ match line starts/ends |
s | DotAll — . matches newlines |
u | Unicode — full Unicode + \p{...} property escapes |
y | Sticky — match starting at lastIndex only |
Common regex patterns — copy-paste ready
Battle-tested patterns for the most-asked validation tasks. Adjust the strictness for your use case — these are pragmatic, not RFC-perfect. Once you have a slug-style pattern dialed in, the URL slug generator applies the same rules end-to-end, and the regex explainer turns any pattern into plain-English documentation for your team.
JS regular expression tester — what makes JavaScript regex different
JavaScript's RegExp is a backtracking NFA engine with a few quirks that trip up developers porting patterns from PHP, Python, or Perl. The g (global) flag is stateful — calling .exec() in a loop advances lastIndex, so reusing a global regex across functions can skip matches. Lookbehind (?<=...) shipped in ES2018 but Safari only added it in 16.4 (March 2023) — patterns that need to support older iOS still need workarounds. matchAll() (ES2020) is the modern way to iterate every match without managing lastIndex yourself. Unicode property escapes (\p{Letter}, \p{Emoji}) require the u flag and unlock proper non-Latin matching. The tester above runs your patterns through this exact engine — if it matches here, it matches in production browsers.
Regexp tester, regex evaluator, regular expression tester — they all mean the same thing
Search-engine variants of the same intent: "regex tester", "regexp tester", "regex evaluator", "regular expression tester", "regex tester javascript", "regex javascript tester", "javascript regex checker", "js regex online" — all five-to-eight-word phrasings ask for the same workflow. Paste a pattern, paste a test string, see matches highlighted, inspect capture groups. The tool above does that and nothing more. The match-count badge updates as you type; named groups appear in the capture-group panel; flag toggles on the right control case sensitivity, multiline behavior, and dotAll mode without rebuilding the regex string by hand. If your pattern has a bug, the error display shows the column where parsing failed — same line/column format Node and modern browsers emit in their stack traces.
JS regex online — what changed in ECMAScript 2024 and 2025
JavaScript regex picked up two new features recently. ES2024 added the v flag ("Unicode sets"), which extends u-flag behavior with set notation — /[\p{ASCII}--[abc]]/v means "ASCII characters except a, b, c". Use it when your pattern needs Unicode property escapes and set difference/intersection. ES2025 finalized RegExp.escape() for safely embedding user input into a pattern (no more manual str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') escape helpers). The tester above runs in your browser's native engine, so you'll see ES2024+ behavior directly — paste a /v-flag pattern and the engine accepts it on Chrome 124+, Firefox 116+, Safari 17.4+.
Regex101 alternative without ads or signup
Regex101 dominates this category but logs your patterns server-side, requires an account to save snippets, and shows ads on free tier. This page does the same job — pattern + flags + test string + capture-group inspection — entirely client-side. No upload, no telemetry on your patterns, no rate limits. If you want a plain-English breakdown of what your pattern matches token-by-token, paste it into the regex explainer next door.
| Use case | Pattern | Notes |
|---|---|---|
| Email (pragmatic) | ^[\w.+-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*$ |
Catches 99% of real emails. Don't use full RFC 5322 — verify deliverability instead. |
| URL (http/https) | ^https?:\/\/[^\s/$.?#].[^\s]*$ |
Loose validation; for strict, use new URL(). |
| IPv4 | ^(?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d)$ |
Validates each octet 0–255. |
| UUID v4 | ^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ |
Add i flag for uppercase support. |
| Hex color | ^#?([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$ |
3-digit, 6-digit, or 8-digit (with alpha). Use i flag. |
| ISO 8601 date (basic) | ^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?)?$ |
Accepts YYYY-MM-DD or full timestamp. |
| Strong password (length + 4 classes) | ^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$ |
Lookahead enforces each character class. |
| Phone (E.164) | ^\+[1-9]\d{1,14}$ |
International standard. No spaces or dashes. |
| Slug (URL-safe) | ^[a-z0-9]+(?:-[a-z0-9]+)*$ |
Lowercase, hyphens between alphanumeric runs. |
Regex in 8 programming languages
JavaScript / TypeScript
// Test, match, replace
const re = /(\w+)@(\w+\.\w+)/g;
"send to a@b.com".match(re); // ['a@b.com']
"a@b.com".replace(re, '[email]'); // '[email]'
// Named groups (ES2018+)
const m = "2026-04-30".match(/^(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})$/);
m.groups.y; // '2026'
// Iterate all matches with positions
for (const m of "abc 123 xyz 456".matchAll(/\d+/g)) {
console.log(m[0], m.index);
}
Python
import re
# search/match/findall/finditer
m = re.search(r'(\w+)@(\w+\.\w+)', 'send to a@b.com')
m.group(0) # 'a@b.com'
m.group(1) # 'a'
# Named groups: (?P<name>...)
m = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})', '2026-04')
m.group('year') # '2026'
# Compile once, reuse many times
pattern = re.compile(r'\d+')
[m.group() for m in pattern.finditer('abc 123 xyz 456')]
PHP (PCRE)
// Match (returns 1, 0, or false)
if (preg_match('/^(\d{4})-(\d{2})$/', '2026-04', $m)) {
echo $m[1]; // '2026'
}
// Find all
preg_match_all('/\d+/', 'abc 123 xyz 456', $matches);
// Replace
$clean = preg_replace('/\s+/', ' ', $messy);
Java
import java.util.regex.*;
Pattern p = Pattern.compile("(\\w+)@(\\w+\\.\\w+)");
Matcher m = p.matcher("send to a@b.com");
if (m.find()) {
System.out.println(m.group(0)); // a@b.com
System.out.println(m.group(1)); // a
}
// Replace
String clean = " multiple spaces ".replaceAll("\\s+", " ");
Go
import "regexp"
// MustCompile panics on invalid pattern (good for static patterns)
re := regexp.MustCompile(`(\w+)@(\w+\.\w+)`)
match := re.FindStringSubmatch("send to a@b.com")
// match[0] = "a@b.com", match[1] = "a"
// All matches
all := re.FindAllString("a@b.com c@d.org", -1)
Rust
use regex::Regex;
let re = Regex::new(r"(\w+)@(\w+\.\w+)").unwrap();
if let Some(caps) = re.captures("send to a@b.com") {
println!("{}", &caps[0]); // a@b.com
}
// Iterate all
for m in re.find_iter("a@b.com c@d.org") {
println!("{}", m.as_str());
}
Ruby
# =~ returns match position or nil
"send to a@b.com" =~ /(\w+)@(\w+\.\w+)/
puts $~[0] # 'a@b.com'
# scan for all matches
"a 1 b 2 c 3".scan(/\d/) # ['1','2','3']
# Named captures via MatchData
m = "2026-04".match(/(?<year>\d{4})-(?<month>\d{2})/)
m[:year] # '2026'
Bash (grep / sed)
# Extended regex with grep -E
echo "abc 123 xyz" | grep -oE '[0-9]+'
# Capture and replace with sed
echo "2026-04-30" | sed -E 's/^([0-9]{4})-([0-9]{2}).*/\1\/\2/'
# Test from a script
if [[ "$input" =~ ^[0-9]+$ ]]; then echo "all digits"; fi
Catastrophic backtracking — and how to avoid it
The single biggest production bug regex causes is catastrophic backtracking: a pattern that takes milliseconds on a 10-character input takes 30+ seconds on a 30-character input. The classic example: ^(a+)+$ against the string "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!". The engine tries 2^33 possible groupings before giving up.
What causes it
- Nested quantifiers:
(a+)+,(a*)*,(.+)+ - Overlapping alternations:
(a|a)*,(a|aa|aaa)+ - Greedy quantifiers + backtracking-friendly suffixes:
".*"in".*"foowhen the input has nofoo
How to avoid it
- Use atomic groups or possessive quantifiers in PCRE:
(?>a+)ora++commit to the match and prevent backtracking. - Be more specific than
.*. If you mean "characters that aren't quotes", use[^"]*not.*. - Anchor with
^and$when validating fixed-length input. Engine bails out faster on mismatch. - Switch engines for untrusted input. Go's
regexpand Rust'sregexuse RE2 — guaranteed linear time, no catastrophic backtracking possible. Lose lookaround & backreferences in exchange for safety. - Set timeouts. .NET 7+ supports
RegexOptions.NonBacktrackingand explicit timeouts. PCRE2 hasJIT_STACKlimits.
Regex best practices for 2026
- Don't validate emails with regex alone. The full RFC 5322 grammar is > 6,000 characters of regex. Use a simple sanity check (
.+@.+\..+) and verify by sending a confirmation link. - Comment complex patterns. JavaScript: prefix lines with
//and concat. Python and PCRE: use thexflag for verbose mode with embedded comments. - Compile once, reuse always. In hot loops, compile the regex outside the loop. Most languages cache automatically; Python's
re.compileand Java'sPattern.compileare explicit. - Use
?:for grouping when you don't need capture. Faster matching, cleaner output.(?:foo|bar)+not(foo|bar)+. - Never use regex to parse HTML or XML. Both are context-free languages. Use a real parser (DOMParser, lxml, BeautifulSoup, jsdom). Regex is fine for finding simple, well-defined patterns inside HTML, just not for parsing structure.
- Use named capture groups when extracting more than 2 values. Self-documenting and safer if the pattern changes.
- Test against edge cases: empty string, all whitespace, Unicode, very long input. Most regex bugs surface at the extremes.
How this regex tester compares to popular alternatives
regex101.com has been the de-facto regex testing reference since 2011. Most other regex testers exist in its shadow. The 2026 differentiation is less about features and more about: which regex flavors you need, whether you want a paired explainer for patterns you didn't write, and whether you'd rather work in a single dev-tool catalog instead of jumping between specialized sites. Here's the honest comparison.
| Capability | FreeDevTool /regex-tester | regex101.com | regexr.com | iHateRegex.io |
|---|---|---|---|---|
| JavaScript regex (ECMAScript) | ✅ Native browser engine | ✅ | ✅ | ✅ |
| PCRE / PCRE2 / Boost flavors | ❌ | ✅ | ❌ | ❌ |
| Python regex flavor | ❌ | ✅ | ❌ | ❌ |
| Java / Golang / Rust regex flavors | ❌ | ✅ | ❌ | ❌ |
| Live match highlighting | ✅ | ✅ | ✅ | ✅ |
| Capture groups breakdown | ✅ With group index colors | ✅ | ✅ | ✅ |
| Substitution / replace mode | ✅ | ✅ | ✅ | ❌ |
| Plain-English pattern explainer | ✅ Dedicated /regex-explainer tool | ✅ Built-in panel | ✅ Built-in panel | ⚠️ Visualization only |
| Catastrophic backtracking warnings | ❌ | ✅ Performance tab | ❌ | ❌ |
| Code generation (regex → Python / JS / etc.) | ❌ Roadmap | ✅ | ❌ | ❌ |
| Save patterns without an account | ✅ Browser localStorage | ⚠️ Account suggested | ⚠️ Account suggested | ✅ |
| Paired with long-form regex reference | ✅ Regex Explainer with 4,000-word guide | ⚠️ Quick cheatsheet | ⚠️ Cheatsheet | ❌ |
| Part of a broader 50-tool catalog | ✅ Plus 4 in-depth guides | ❌ Regex only | ❌ Regex only | ❌ Regex only |
| Paid / Pro tier exists | ❌ Free only | ✅ ~$5/mo | ❌ | ❌ |
When to pick which tool
- Pick FreeDevTool /regex-tester when: you're testing JavaScript regex (the most common case in modern web work), you want a paired explainer tool to understand someone else's pattern, you don't want to create an account, and you'd benefit from being part of a broader 50-tool catalog you already use.
- Pick regex101 when: you need PCRE / PCRE2 / Python / Java / Go regex flavors, deep optimization analysis (catastrophic-backtracking detection on the Performance tab), or one-click code generation in your target language. regex101 is the gold standard for those use cases — this guide isn't trying to replace it.
- Pick regexr.com when: you want a friendlier visual learning experience for JavaScript regex with side-by-side cheatsheet.
- Pick iHateRegex.io when: you want to find a pre-built pattern for a common case (email, phone, URL) without writing your own.
This is an honest comparison, not a marketing pitch. If you're tuning a Python production regex with concern about backtracking, regex101's Performance tab is the right tool. Where FreeDevTool genuinely differentiates is the tester + paired explainer + 4,000-word reference + 50-tool ecosystem combination — useful when regex work is one task in a longer dev workflow, especially in JavaScript codebases.
Frequently compared regex tools
If you're researching regex tester alternatives, you're probably also looking at: regex101.com, regexr.com, iHateRegex.io, pythex.org (Python only), rubular.com (Ruby only). The regex101 dominance reflects 13 years of authority and a team focused entirely on regex; pick whichever specializes in your specific need.