Regular Expressions: A Practical Guide With Examples

Regular expressions are one of those skills that feel intimidating until they click — and then you wonder how you ever wrote string-processing code without them. They appear in every serious codebase: input validation, log parsing, search-and-replace, URL routing, lexers. This guide takes you from zero to fluent with practical examples throughout.

What Is a Regular Expression?

A regular expression (regex) is a pattern that describes a set of strings. You write the pattern, and the regex engine finds every string that matches it. The same pattern works across languages — JavaScript, Python, PHP, Go, Java — because they all implement the same core specification (with minor dialect differences).

Two things regex is not: it is not a programming language, and it does not execute in sequence. The engine reads your pattern and tests it against the input all at once using a finite automaton internally.

Literal Characters and Metacharacters

The simplest regex is just a literal string. The pattern hello matches the exact text "hello" anywhere in the input. Most characters match themselves — but a small set have special meaning and must be escaped with a backslash if you want them literally:

. ^ $ * + ? { } [ ] \ | ( )

For example, to match a literal dot (like in a file extension), write \. not .. A bare dot matches any single character except a newline.

# Matches: "cat", "bat", "hat", "3at" — any char before "at"
.at

# Matches only a literal dot
\.

Character Classes

Character classes let you match one character from a defined set. You write them inside square brackets.

[aeiou]      # any vowel
[a-z]        # any lowercase letter
[A-Z]        # any uppercase letter
[0-9]        # any digit (same as \d)
[a-zA-Z0-9]  # any alphanumeric character
[^aeiou]     # any character that is NOT a vowel (^ negates)

Regex also provides shorthand character classes for the most common sets:

\d    # digit: [0-9]
\D    # non-digit: [^0-9]
\w    # word character: [a-zA-Z0-9_]
\W    # non-word character
\s    # whitespace: space, tab, newline
\S    # non-whitespace

Quantifiers

Quantifiers control how many times the preceding element must appear.

*       # zero or more
+       # one or more
?       # zero or one (makes it optional)
{3}     # exactly 3 times
{2,5}   # between 2 and 5 times (inclusive)
{2,}    # 2 or more times

By default, quantifiers are greedy — they match as many characters as possible. Add a ? after the quantifier to make it lazy (match as few as possible):

<.+>    # greedy: matches entire "<b>text</b>" as one match
<.+?>   # lazy: matches "<b>" and "</b>" as separate matches

Anchors

Anchors don't match characters — they match positions in the string.

^   # start of string (or start of line with multiline flag)
$   # end of string (or end of line with multiline flag)
\b  # word boundary (between \w and \W)
\B  # non-word boundary
^\d+$       # entire string must be digits only
\bword\b    # matches "word" as a whole word, not inside "password"

Groups and Alternation

Parentheses group parts of a pattern and capture the matched text for later use.

(abc)           # capture group — captures "abc"
(?:abc)         # non-capturing group — groups without capturing
(?<year>\d{4}) # named capture group — access as match.groups.year

The pipe | acts as OR between alternatives:

cat|dog         # matches "cat" or "dog"
(jpg|jpeg|png)  # matches any of the three extensions

Named capture groups are especially useful when extracting structured data from text:

(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})

# Applied to "2026-06-17":
# match.groups.year  → "2026"
# match.groups.month → "06"
# match.groups.day   → "17"

Flags

Flags modify how the entire pattern behaves. They are appended after the closing slash in regex literals.

g  # global — find all matches, not just the first
i  # case insensitive — "Hello" matches "hello"
m  # multiline — ^ and $ match start/end of each line
s  # dot-all — dot (.) matches newlines too
/hello/gi   # matches "Hello", "HELLO", "hello" — all occurrences
/^\d+/m     # matches a line that starts with digits

Real-World Examples

Email Validation (basic)

^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$

Matches: user@example.com, user.name+tag@sub.domain.co. Note: a truly complete email regex is several hundred characters long. For production use, validate server-side and send a confirmation email instead of relying on regex alone.

Extract URLs from Text

https?:\/\/[\w\-._~:/?#[\]@!$&'()*+,;=%]+

Match a Version Number (e.g. 1.2.3)

\d+\.\d+\.\d+

Parse an Apache Log Line

^(?<ip>\S+)\s\S+\s\S+\s\[(?<time>[^\]]+)\]\s"(?<method>\w+)\s(?<path>\S+)

Extracts the IP address, timestamp, HTTP method, and request path from each line of an Apache access log.

Find Hex Color Codes

#[0-9a-fA-F]{3,6}\b

Common Mistakes

  • Forgetting to escape the dot. .com matches "Xcom", "1com", etc. Write \.com for a literal dot.
  • Using greedy quantifiers when lazy is needed. Parsing HTML with <.+> will swallow entire lines. Use <.+?> or better — use a proper HTML parser.
  • Matching at word boundaries. The pattern cat matches inside "concatenate". Use \bcat\b if you want the whole word.
  • Catastrophic backtracking. Patterns like (a+)+b can hang on input like "aaaaaac" because the engine tries exponential combinations. Prefer atomic groups or possessive quantifiers in critical-path code.
  • Parsing structured formats with regex. Don't parse HTML, JSON, or XML with regex. Use the right parser for the job.

Want to test and debug a regex pattern right now with live match highlighting?

Try the Free Regex Tester

Frequently Asked Questions

What is a regular expression?

A regular expression (regex) is a sequence of characters that defines a search pattern. It is used to match, extract, or replace text that follows a specific structure — such as email addresses, phone numbers, or log entries. Regex is supported in virtually every programming language, including JavaScript, Python, PHP, Java, and Go.

What does the g flag do in regex?

The g (global) flag tells the regex engine to find all matches in the input, not just the first one. Without g, most regex functions stop after the first match. In the Creesol Regex Tester, the g flag is always on so you can see all matches at once.

What is the difference between + and * in regex?

+ means "one or more" — it requires at least one match. * means "zero or more" — it matches even if the pattern appears zero times. For example, \d+ matches one or more digits, while \d* matches zero or more digits and will match the empty string.

How do I match a literal dot in regex?

In regex, a bare dot . is a metacharacter that matches any single character (except newline). To match a literal dot — such as in a file extension or domain name — escape it with a backslash: \.. For example, \.php matches only ".php" literally.

What is a capture group in regex?

A capture group is a section of a regex pattern wrapped in parentheses. It captures the matched text so you can extract or reuse it. For example, (\d{4})-(\d{2})-(\d{2}) applied to "2026-06-17" captures three groups: "2026", "06", and "17". Named capture groups ((?<name>pattern)) let you reference groups by name instead of index number.