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.
.commatches "Xcom", "1com", etc. Write\.comfor 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
catmatches inside "concatenate". Use\bcat\bif you want the whole word. - Catastrophic backtracking. Patterns like
(a+)+bcan 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
CREESOL