Command Palette

Search for a command to run...

Regex Builder: Test Regular Expressions Visually (With a Cheat Sheet)

Regex Builder: Test Regular Expressions Visually (With a Cheat Sheet)

T
Toolz Team
|Jul 1, 2026|17 min read

I have a confession that will make regex purists wince: for the first few years of my career, I wrote regular expressions by copy-pasting from Stack Overflow, changing one character, running the code, seeing it fail, and repeating until it happened to work. I didn't understand the patterns โ€” I bullied them into submission. It was slow, and worse, the patterns I shipped were fragile in ways I couldn't see.

What fixed that wasn't reading the theory (though I eventually did). It was a visual tester โ€” a box where I type the pattern, a box where I paste test strings, and matches that light up the instant I change something. Suddenly I could see why \d+ grabbed more than I wanted, or why my email pattern rejected a perfectly valid address. Regex stopped being a guessing game and became a tight feedback loop.

That's exactly what the Regex Builder on toolz.dev is. You write a pattern, drop in test data, toggle flags, and watch the matches and captured groups highlight in real time. It runs entirely in your browser, so the log lines or user data you're testing against never get uploaded anywhere. This guide is the regex reference I wish I'd had back then โ€” the patterns I actually reuse, a full cheat sheet, and the one mistake that can take down a production server.

TL;DR: Paste your pattern and test strings into the Regex Builder and toggle the g/i/m flags to see matches highlight live. Start simple, add constraints to kill false positives, and test adversarial inputs to avoid catastrophic backtracking. For extracted data, pair it with the JSON Formatter and Base64 Converter. All client-side, all free.


What Exactly Is a Regular Expression?

A regular expression โ€” regex or regexp โ€” is a compact string that describes a search pattern. Instead of "find the word cat," you can say "find any five-digit number," "find anything that looks like an email," or "find every line that starts with ERROR." Nearly every language and editor supports them, which is what makes the skill so portable: learn it once and you use it in JavaScript, Python, your grep, and your IDE's find-and-replace.

The concept comes out of formal language theory from the 1950s, and Ken Thompson wired it into computing in the 1960s for text editing. That history matters for one practical reason I'll come back to: "regular" languages have limits, which is why regex genuinely cannot parse nested structures like HTML no matter how clever you get.

Where I reach for regex in a normal week: validating user input before it hits the database, pulling fields out of unstructured log lines, doing find-and-replace across a whole codebase that a plain search can't express, and filtering data during a migration. The Regex Builder is where I prototype every one of those before it goes anywhere near real code.

Here are the fundamental building blocks โ€” the alphabet you assemble patterns from:

Syntax Meaning Example Matches
. Any character except newline h.t hat, hot, hit
\d Any digit (0-9) \d{3} 123, 456
\w Word char (a-z, A-Z, 0-9, _) \w+ hello, test_123
\s Any whitespace hello\sworld hello world
^ Start of string ^Hello Hello at the start
$ End of string end$ end at the end
* Zero or more ab*c ac, abc, abbc
+ One or more ab+c abc, abbc
? Zero or one colou?r color, colour
{n} Exactly n times \d{4} 2026
{n,m} Between n and m \d{2,4} 12, 123, 1234
[abc] Character class [aeiou] any vowel
[^abc] Negated class [^0-9] any non-digit
(...) Capture group (hello) captures "hello"
a|b Alternation (or) cat|dog cat or dog

Which Regex Patterns Should Every Developer Keep Handy?

These are the ones I've tested, broken, fixed, and now keep in a personal snippet file. Copy them straight into the Regex Builder and throw your own edge cases at them.

Email (the practical kind)

^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$

This matches [email protected] and [email protected], and rejects @example.com or user@com. Here's the important honesty, though: the full email grammar in RFC 5322 is monstrously complex โ€” it technically allows quoted strings and comments almost nobody uses. Don't chase 100% RFC compliance with a regex. Match the practical 99% with the pattern above, then confirm the address is real by sending a verification email. That's the mistake I see most: teams burning days on an "airtight" email regex that still can't tell a real inbox from a typo.

URL

^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$

Matches https://toolz.dev and http://www.example.com/path?query=value; rejects ftp://... and plain text.

US phone number

^(\+1)?[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$

Flexible enough for 555-123-4567, (555) 123-4567, +1 555.123.4567, and 5551234567.

IPv4 address

^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$

The nested alternation is what enforces that each octet stays in 0โ€“255, so it correctly rejects 999.999.999.999.

Strong password check

^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$

Requires at least 8 characters with a lowercase, an uppercase, a digit, and a symbol. The (?=...) lookaheads each assert one condition without consuming characters โ€” a neat trick worth understanding.

ISO date (YYYY-MM-DD)

^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$

Matches 2026-07-11, rejects 2026-13-01 and 2026-02-32.

HEX color

^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$

Matches #4466EE, #abc, #000000.


The Regex Cheat Sheet

Keep this pinned. It's the reference I check most.

Anchors

Pattern Description
^ Start of string (or line in multiline mode)
$ End of string (or line in multiline mode)
\b Word boundary
\B Non-word boundary

Quantifiers

Pattern Description
* 0 or more (greedy)
+ 1 or more (greedy)
? 0 or 1 (greedy)
*? +? ?? Lazy versions โ€” match as little as possible
{n} {n,} {n,m} Exactly n / n or more / between n and m

Character classes

Pattern Description
[abc] a, b, or c
[^abc] Not a, b, or c
[a-z] [A-Z] [0-9] Ranges
\d \D Digit / non-digit
\w \W Word char / non-word char
\s \S Whitespace / non-whitespace

Groups and lookaround

Pattern Description
(abc) Capturing group
(?:abc) Non-capturing group
(?<name>abc) Named capturing group
(?=abc) / (?!abc) Positive / negative lookahead
(?<=abc) / (?<!abc) Positive / negative lookbehind

Flags

Flag Description
g Global โ€” find all matches
i Case-insensitive
m Multiline โ€” ^ and $ match line boundaries
s Dotall โ€” . matches newlines
u Unicode support

How Do You Build and Debug a Pattern Without Losing an Hour?

My process is boring on purpose, because boring is repeatable:

  1. Start with the simplest thing that matches one real example. Don't try to handle every case at once.
  2. Paste both positive and negative test strings into the Regex Builder โ€” things that should match and things that must not.
  3. Tighten to kill false positives. Add anchors (^, $) so the pattern matches the whole string, and swap . for specific classes like [a-z] or [^,].
  4. Throw adversarial input at it โ€” empty strings, huge inputs, Unicode, and literal regex characters as data.
  5. Only then optimize.

When something misbehaves, it's almost always one of three things. If it matches too much, your quantifiers are greedy โ€” make them lazy (*?) or your classes more specific. If it matches too little, you probably need the i flag or forgot to escape a special character. If it matches nothing at all, check for unescaped metacharacters (., *, +, (, [, {, etc.) meant to be literal, or invisible characters like tabs hiding in your test string.


What Is Catastrophic Backtracking, and Why Should You Fear It?

This is the section I'd tattoo on new developers if I could. Early on I shipped an input-validation regex that looked completely innocent, and one afternoon a single request pegged a CPU core to 100% and stayed there. The pattern was the problem.

When a regex engine can't find a match, it backtracks โ€” it retreats and tries other ways to satisfy the pattern. Certain shapes make the number of paths explode exponentially. The textbook example:

^(a+)+$

Feed that an input like aaaaaaaaaaaaaaaaaaaab (a run of a ending in a b), and the nested quantifiers give the engine an astronomical number of ways to divide up the as, all of which it tries before concluding "no match." Your app freezes. This is a real denial-of-service class called ReDoS (Regular Expression Denial of Service), and attackers do exploit it.

How to stay safe:

  1. Never nest quantifiers. (a+)+, (a*)*, and (a+)* are red flags.
  2. Use atomic groups or possessive quantifiers where the engine supports them: (?>a+) or a++.
  3. Be specific. [a-z]+ backtracks less than .+ because it has fewer paths to explore.
  4. Anchor your patterns so the engine fails fast on non-matching input.
  5. Test with strings that almost match but fail at the very end โ€” that's the worst case for backtracking.

If you're on Go, this problem simply doesn't exist, which brings me to the next section.


How Does Regex Differ Between Languages?

The core syntax travels well, but the details bite. These are the differences I've actually tripped over.

JavaScript:

const regex = /\d{3}-\d{4}/g;
const pattern = new RegExp('\\d{3}-\\d{4}', 'g'); // note the doubled backslashes
'555-1234'.match(regex);              // ["555-1234"]
const m = '2026-07-11'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
m.groups.year;                        // "2026"

Watch out: lookbehind only arrived in ES2018, \d matches ASCII digits only, and the g flag makes .test() stateful via lastIndex โ€” a subtle bug source in loops.

Python:

import re
pattern = re.compile(r'\d{3}-\d{4}')      # raw strings avoid double-escaping
pattern.findall('Call 555-1234 or 555-5678')  # ['555-1234', '555-5678']

Always use raw strings (r'...'). Remember re.match only anchors at the start โ€” use re.search to find anywhere. And re.VERBOSE lets you write multi-line commented patterns, which is a lifesaver for complex ones.

PHP: patterns need delimiters ('/\d{3}-\d{4}/'), it uses the powerful PCRE engine, and preg_match returns 1, 0, or false on error โ€” so check with ===.

Go: uses the RE2 engine, which deliberately drops lookaheads, lookbehinds, and backreferences in exchange for a guarantee of linear-time matching. That means no catastrophic backtracking is even possible โ€” a genuinely different tradeoff worth knowing when you pick a language for untrusted-input matching.


A Real Example: Parsing an Apache Log Line

Here's the kind of thing regex is genuinely great at. A standard Apache access log line looks like:

192.168.1.1 - frank [11/Jul/2026:10:27:10 -0500] "GET /api/users HTTP/1.1" 200 1234

This pattern pulls it apart:

^(\S+) \S+ (\S+) \[([^\]]+)\] "(\S+) (\S+) (\S+)" (\d{3}) (\d+)$

Paste both into the Regex Builder and each captured group lights up separately:

Group Content Example
1 IP address 192.168.1.1
2 Username frank
3 Timestamp 11/Jul/2026:10:27:10 -0500
4 HTTP method GET
5 Request path /api/users
6 HTTP version HTTP/1.1
7 Status code 200
8 Response size 1234

Once the groups line up in the tester, translating it to a re.findall in Python or a match() in JavaScript is trivial โ€” and you already know it works.


Frequently Asked Questions

What is a regex builder?

A regex builder is an interactive tool where you type a regular expression and immediately see it matched against test strings, with matches and captured groups highlighted. It replaces the slow write-run-fail-rewrite loop in your code with a live one. The Regex Builder on toolz.dev does this entirely in your browser, so test data stays on your machine.

Are regex patterns identical in every programming language?

The core syntax is nearly identical, but the details differ enough to cause bugs. Older JavaScript lacked lookbehind, Go's RE2 engine has no lookaheads or backreferences at all, and Python names groups with (?P<name>...) in some contexts. Always confirm a pattern in the language you're actually targeting rather than assuming portability.

How do I validate an email address with regex?

Use a practical pattern like ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$, which handles the vast majority of real addresses. Do not attempt full RFC 5322 compliance in a regex โ€” the grammar is far too complex and you'll still reject valid addresses or accept typos. Pair the regex with a verification email to confirm the inbox actually exists.

Why does my regex freeze the application?

Almost always catastrophic backtracking. Patterns with nested quantifiers like (a+)+ create an exponential number of matching paths when they meet input that nearly matches but ultimately fails, and the engine tries them all. Remove nested quantifiers, prefer specific character classes over .+, and add anchors so the engine can fail quickly.

Can I use regex to parse HTML or JSON?

No, not for anything beyond trivial extraction. HTML and JSON are not regular languages โ€” they allow arbitrary nesting that regex fundamentally cannot track. Use a real parser: DOMParser or Cheerio for HTML, and JSON.parse or a JSON tool for JSON. Regex is the wrong tool the moment structure nests.

What's the difference between greedy and lazy matching?

Greedy quantifiers (*, +, ?) grab as much as possible and backtrack if needed; lazy ones (*?, +?, ??) grab as little as possible and expand only if forced. Against <b>bold</b>, greedy <.*> swallows the whole string while lazy <.*?> stops at the first <b>. Choosing the right one is often the fix when a pattern matches too much.

How do I escape special characters in regex?

Put a backslash in front of any metacharacter you mean literally: \., \*, \+, \?, \(, \), \[, \], \{, \}, \^, \$, \|, and \\. Most languages also offer a helper to escape a whole string for you โ€” re.escape() in Python, for example โ€” which is safer than escaping by hand when the string comes from user input.

What do the regex flags g, i, and m mean?

g (global) finds every match instead of stopping at the first, i (ignore case) makes the pattern case-insensitive, and m (multiline) makes ^ and $ match at every line break rather than only the string's start and end. They combine freely, so gim does all three. A frequent gotcha in JavaScript: reusing a g regex across calls carries lastIndex between them, which makes test() alternate true and false โ€” recreate the pattern or reset lastIndex.

What is a lookahead in regex?

A lookahead asserts that something does or doesn't follow, without consuming it. foo(?=bar) matches foo only when bar comes next; foo(?!bar) matches only when it doesn't. Lookbehind ((?<=...), (?<!...)) does the same backwards, and has landed in modern JavaScript, Python and PCRE โ€” but not in Go's RE2, which rejects both outright. Password rules are the classic use: ^(?=.*\d)(?=.*[a-z]).{8,}$ stacks assertions so each requirement is checked independently at the same position.

How do I match a phone number with regex?

For a specific format, be explicit rather than clever: ^\(\d{3}\) \d{3}-\d{4}$ matches (555) 123-4567. To tolerate varied separators, allow optional ones like ^\+?1?[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$. Phone numbering is messy across countries, so validate only the format you actually accept and normalize to digits before storing โ€” build and test the pattern live before trusting it.


Wrapping Up

Regex went from my most-dreaded skill to one of my most-used the moment I started building patterns in a live tester instead of guessing in my editor. Start simple, test against real and adversarial input, respect catastrophic backtracking, and keep a personal library of patterns you trust.

The Regex Builder on toolz.dev makes that loop fast, with real-time matching, group highlighting, and flag toggles โ€” all running in your browser so your test data stays private. When your pattern extracts JSON, hand it to the JSON Formatter; when it captures a Base64 blob, decode it with the Base64 Converter. Or browse all 600+ free tools over at toolz.dev.

Comments

0 comments

0/2000 characters

No comments yet. Be the first to share your thoughts!