Developer ToolsRegex

Regular Expressions Explained: Syntax, Flags, Patterns & How to Test Them

Everything you need to write, read, and debug regular expressions — from the first literal character to catastrophic-backtracking pitfalls.

What are regular expressions?

A regular expression (shortened to regex or regexp) is a mini-language for describing text patterns. You write a compact formula, hand it to a regex engine, and the engine scans a string reporting where — and how often — the pattern matches. One short expression like \d{4}-\d{2}-\d{2} can find every ISO date in a million-line log file in milliseconds.

Regular expressions are available in virtually every programming language — JavaScript, Python, Java, Go, Ruby, Perl, PHP — and in command-line tools like grep, sed, and most code editors. They are used for form validation, search-and-replace, log parsing, data extraction, and URL routing. The syntax differs slightly between flavours (PCRE, Python re, Java, JavaScript), but the core concepts covered in this guide apply everywhere. The Regex Tester on sourcecodestack runs the JavaScript (ECMAScript) engine — the same engine used by Node.js and every modern browser.

Core syntax building blocks

Literals and the dot

The simplest pattern is a literal character: cat matches the exact three-character sequence cat wherever it appears in the string. Most characters match themselves. The exceptions are the twelve metacharacters: . * + ? ^ $ { } [ ] ( ) | \. To match one of these literally, escape it with a backslash: \. matches a real period.

The dot . matches any single character except a newline (unless the s dotAll flag is set). Use it sparingly — a bare dot is very permissive and can cause accidental matches.

Character classes

Square brackets define a character class — a set of characters, any one of which can match at that position. [aeiou] matches any single vowel. [a-z] uses a range shorthand for all lowercase ASCII letters. A caret at the start negates the class: [^0-9] matches anything that is not a digit.

Shorthand classes cover the most common sets and work inside or outside square brackets:

Anchors

Anchors assert a position rather than consuming a character.

For example, \bcat\b matches the whole word cat but not catch or concatenate.

Quantifiers

Quantifiers specify how many times the preceding element must appear.

By default all quantifiers are greedy — they match as many characters as possible while still allowing the overall pattern to succeed. Append a ? to make them lazy (match as few as possible): .*? matches the shortest possible run of any characters.

Groups and alternation

Parentheses serve two purposes: grouping sub-patterns so a quantifier applies to the whole group, and capturing the matched text for later use.

The pipe character | is alternation — it means "this or that": cat|dog matches either cat or dog. Enclose alternation inside a group when you only want the choice to apply to part of the pattern: I like (?:cats|dogs).

Backreferences

A backreference refers back to a previously captured group within the same pattern. \1 matches the same text that group 1 captured. This is useful for finding repeated words: \b(\w+)\s+\1\b matches doubled words like the the or very very. In replacement strings, back-references use the $1 syntax (JavaScript) or \1 (most other flavours).

Lookahead and lookbehind

Lookarounds let you match text only when it is preceded or followed by another pattern — without including that surrounding text in the match itself. They are zero-width assertions: they consume no characters.

Example: match any number followed by the word "px" without capturing "px": \d+(?=px). Given the string font-size: 16px, it matches 16 but not px.

The six JavaScript flags

Flags modify how the engine interprets the pattern. In JavaScript they are appended after the closing slash: /pattern/gi. In the Regex Tester you toggle each flag with a button.

FlagNameEffect
gglobalFind all matches instead of stopping at the first.
iignoreCaseMake the match case-insensitive (a matches A).
mmultiline^ and $ match the start and end of each line, not just the whole string.
sdotAllThe dot . matches newline characters too.
uunicodeEnables full Unicode matching; required for \p{…} property escapes and correct handling of code points above U+FFFF.
ystickyMatch only at the exact position stored in lastIndex; does not scan forward.

The most commonly used combination for a global, case-insensitive search is gi. Add m when your test string contains multiple lines and you want ^/$ to match each line individually.

Capture groups vs. non-capturing groups

Every pair of plain parentheses creates a numbered capture group. For a complex pattern with many groups, those numbers pile up quickly and replacement strings like $4 become hard to maintain. Two strategies help:

The Regex Tester shows both numbered and named groups in a table beneath the matches, so you can immediately see what each pair of parentheses is capturing.

Regex syntax quick-reference

TokenMatchesExample
.Any character except newline (without s flag)/c.t/ → 'cat', 'cut', 'c9t'
\dDigit 0–9/\d+/ → '42', '0', '2026'
\w[A-Za-z0-9_]/\w+/ → 'hello', 'var_1'
\sWhitespace character/\s+/ → spaces, tabs, newlines
[abc]Any of a, b, or c/[aeiou]/ → vowels
[^abc]Anything except a, b, or c/[^0-9]/ → non-digits
^Start of string (or line with m)/^Hello/ → lines starting with Hello
$End of string (or line with m)/!$/ → lines ending with !
\bWord boundary/\bcat\b/ → 'cat' but not 'catch'
*0 or more (greedy)/a*/ → '', 'a', 'aaa'
+1 or more (greedy)/a+/ → 'a', 'aaa'
?0 or 1 (optional)/colou?r/ → 'color', 'colour'
{n,m}Between n and m times/\d{2,4}/ → '12', '123', '1234'
*? / +? / ??Lazy (match as few as possible)/a+?/ on 'aaa' → first 'a' only
(abc)Capture group/(\ w+)@/ captures username
(?:abc)Non-capturing group/(?: abc)+ groups without storing
(?<n>abc)Named capture group(?<year>\d{4})
a|bAlternation (a or b)/cat|dog/ → 'cat' or 'dog'
(?=...)Positive lookahead/\d+(?=px)/ → numbers before px
(?!...)Negative lookahead/\d+(?!px)/ → numbers not before px
(?<=...)Positive lookbehind/(?<=\$)\d+/ → digits after $
(?<!...)Negative lookbehind/(?<!\$)\d+/ → digits not after $
\1Backreference to group 1/(\ w+) \1/ → doubled words

Common real-world patterns explained

The patterns below are widely used starting points. No single regex fits every possible edge case for a format as open-ended as an email address — treat them as sensible defaults and adjust them for your validation rules. Paste each one into the Regex Tester to explore how they behave on your own data.

Email address

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

Breaking this down: the local part before the @ allows letters, digits, dots, underscores, percent signs, plus signs, and hyphens. After the @ comes the domain — letters, digits, dots, and hyphens — followed by a dot and a TLD of at least two letters. The anchors ^ and $ ensure the whole string must match. This pattern does not handle international domain names (IDN) or quoted local parts; for production use, send a confirmation email rather than relying solely on regex.

URL

https?:\/\/[^\s/$.?#].[^\s]*

The s? makes the scheme match both http and https. The escaped slashes are necessary in patterns that use delimiter syntax (like JavaScript literals). The host part uses a negative character class to reject whitespace and common characters that cannot start a host. Everything after is consumed up to the next whitespace. This pattern finds URLs inside prose but is intentionally loose — it will not validate whether a URL is truly reachable.

Phone number (flexible)

[\+]?[(]?[0-9]{1,4}[)]?[-\s\.]?[(]?[0-9]{1,3}[)]?[-\s\.]?[0-9]{3,4}[-\s\.]?[0-9]{3,9}

Phone formats vary enormously worldwide. This flexible pattern accepts an optional leading +, optional parentheses around the country code, and separators that can be hyphens, spaces, or dots between digit groups. If you only need North-American 10-digit numbers in standard format you can use the simpler \(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}.

ISO date (YYYY-MM-DD)

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

The month is constrained to 01–12 and the day to 01–31 using alternation inside the groups. Note that regex cannot validate calendar logic like "February has at most 28 or 29 days" — that requires code after the match.

Whitespace cleanup

\s+

Replace this pattern with a single space (" ") using the g flag to collapse all runs of whitespace into one. Trim the ends with ^\s+|\s+$ (replace with empty string) or just use .trim() in JavaScript for the trimming step.

Hex colour code

#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})\b

This matches both six-digit (#1a2b3c) and three-digit shorthand (#fff) hex colours. The alternation tries the longer form first; if that fails it tries the shorter one. The word boundary \b prevents matching an 8-digit hash as a hex colour.

How to test and debug a regular expression

Writing regex iteratively is far easier than trying to build the perfect pattern on the first attempt. A structured workflow saves time:

  1. Start with a concrete example. Identify two or three strings you expect to match and two or three you expect to reject. Keep them in the test string field.
  2. Build the pattern piece by piece. Start with the most distinctive literal or character class. Confirm it matches the first example before adding anchors or quantifiers.
  3. Use a live tester. The Regex Tester highlights every match inline as you type, so you immediately see when you have over- or under-matched. The capture group table shows you exactly what each pair of parentheses captured.
  4. Check edge cases. Add strings with unusual spacing, different cases, Unicode characters, and empty inputs.
  5. Test the replace string. If you are building a search-and-replace, use the Replace panel to confirm your back-references reconstruct the output correctly before putting the pattern in production code.
  6. Read the error message. If the pattern field shows an error banner, the engine has told you exactly where the syntax is invalid. Common causes: unescaped special characters, unbalanced parentheses, or invalid quantifier ranges like {5,2}.

Performance and catastrophic backtracking

Most regex patterns run in microseconds. But a poorly constructed pattern can bring a server to its knees on certain inputs, a vulnerability sometimes called ReDoS (Regular Expression Denial of Service). Understanding why this happens is essential for any developer writing regex used in production.

How backtracking works

NFA-based regex engines (which includes JavaScript, PCRE, Python, and Java) try to match a pattern by advancing through the string one character at a time. When a greedy quantifier consumes as much as possible but the rest of the pattern then fails, the engine backtracks — it gives back the last character the quantifier consumed and tries again. For a well-designed pattern this happens a small number of times. For a poorly designed one, the number of attempts can be exponential in the length of the input.

The classic catastrophic example is (a+)+$ applied to a string like aaaaaaaaab. The outer + and the inner a+ can divide the as among themselves in exponentially many ways, and the engine tries each combination when the final $ fails to match the trailing b.

How to avoid it

Using the sourcecodestack Regex Tester

The Regex Tester is a free, client-side tool. Your pattern and test text stay in your browser and are never sent to a server — particularly useful when you are working with sensitive log files, customer data, or proprietary code.

Key things the tool shows you that plain console.log does not:

Summary

Regular expressions reward the time spent learning them many times over. A pattern that would take fifty lines of character-by-character code collapses to one line of regex. The key is to build them incrementally: start with literals, add character classes and quantifiers, layer in groups and anchors, and always verify against real data. Keep patterns readable by choosing named groups over numbered ones and non-capturing groups when capture is unnecessary. And whenever a pattern will run against untrusted input, test it against adversarial strings to confirm it cannot be driven into catastrophic backtracking.

Open the Regex Tester and start experimenting — seeing matches highlighted in real time as you type is the fastest way to turn regex theory into fluent practice.