Dev

Regular Expressions (Regex) for Beginners

A regular expression — regex for short — is a tiny pattern language for describing text. Once it clicks, you can find, validate, and reshape text in seconds instead of writing pages of string-handling code. This guide starts from the very first concept and builds up to two patterns you can actually use.

What regex actually is

A regular expression is a string that describes a set of strings. Instead of saying "find the word cat," you can say "find any three-letter word," or "find anything that looks like a phone number." A regex engine takes your pattern and an input string and reports where — or whether — the pattern matches. The three jobs you will use it for most are searching (does this text contain a match?), validation (does this whole string fit the shape of an email or a date?), and find-and-replace (swap every match for something else). Editors, command-line tools like grep, and nearly every programming language understand regex.

Literal characters vs metacharacters

The simplest pattern is just text. The regex cat matches the letters c, a, t in sequence — those are literal characters, each matching itself. The power comes from metacharacters: symbols that mean something special instead of matching themselves. For example . does not mean a period; it means "any single character." Because of this dual life, when you want a metacharacter to match its own symbol you must escape it with a backslash. So \. matches a literal dot, and \$ matches a literal dollar sign.

The core building blocks

Most patterns are assembled from a small set of tokens. These are the ones worth memorizing first:

TokenMeaningExample
.Any single character (except newline)c.t matches cat, cut, c9t
\dAny digit, 0–9\d\d matches 42
\wA word character: letter, digit, or underscore\w+ matches user_1
\sAny whitespace (space, tab, newline)a\sb matches a b
^Start of the string (or line)^Hi matches Hi there
$End of the string (or line)end$ matches the end
[abc]Character class: any one listed character[aeiou] matches one vowel
[^abc]Negated class: any character not listed[^0-9] matches a non-digit
[a-z]Range inside a class[a-z] matches one lowercase letter

Two handy notes: inside a character class most metacharacters lose their power, so [.] simply means a literal dot. And the uppercase versions invert the shorthand — \D is "not a digit," \W is "not a word character," \S is "not whitespace."

Quantifiers: how many times

A token on its own matches exactly once. Quantifiers change that. They attach to the item immediately before them and control repetition:

  • * — zero or more (ab* matches a, ab, abbb)
  • + — one or more (ab+ matches ab but not a)
  • ? — zero or one, i.e. optional (colou?r matches color and colour)
  • {n} — exactly n times (\d{4} matches a four-digit year)
  • {n,m} — between n and m times (\d{2,4} matches 2 to 4 digits)

By default quantifiers are greedy: they grab as much text as they can while still letting the rest of the pattern match. That sometimes overshoots. Given the input <a><b>, the pattern <.*> matches the entire <a><b> because .* greedily swallows everything up to the last >. Append a ? to make a quantifier lazy — it then matches as little as possible. So <.*?> matches just <a>. This greedy-versus-lazy distinction is one of the most common sources of confusion.

Groups and alternation

Parentheses ( ) bundle several tokens into one unit. That lets you apply a quantifier to the whole group: (ab)+ matches ab, abab, ababab. The pipe | means alternation — "either side." So cat|dog matches cat or dog, and gr(a|e)y matches both gray and grey.

Parentheses also capture the text they match so you can reuse it. Capture groups are numbered from left to right starting at 1. A backreference like \1 matches the same text the first group captured: (\w+)\s\1 finds a doubled word such as the the. In replacements, many tools reference captures as $1 or \1, which is how you reorder or reformat matched text.

Anchors and word boundaries

Anchors do not match characters — they match positions. ^ ties the pattern to the start and $ to the end, which is essential for validation: ^\d{5}$ means "the entire string is exactly five digits," whereas plain \d{5} would also accept five digits buried inside a longer string. The word boundary \b marks the edge between a word character and a non-word character. \bcat\b matches cat as a whole word but not the cat inside category.

Worked example 1: a pragmatic email pattern

Matching every valid email perfectly is famously hard — the official specification allows surprising things. For most real-world validation a pragmatic pattern is far more useful than a "correct" one. Here is a reasonable starting point:

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

Reading it left to right: ^ anchors to the start. [\w.+-]+ matches one or more characters allowed in the local part — letters, digits, underscore, dot, plus, hyphen. @ is a literal at-sign. [\w-]+ matches the domain name. \. is an escaped literal dot before the extension. [a-z]{2,} requires a top-level domain of at least two letters, and $ anchors to the end so the whole string must fit. This accepts jane.doe+news@example.com and rejects jane@@example. It is intentionally lenient; treat it as a shape check, then confirm deliverability some other way.

Worked example 2: a US phone number

US phone numbers appear in many styles: (555) 123-4567, 555-123-4567, 555.123.4567, or 5551234567. One pattern can cover them all by making the separators optional:

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

Piece by piece: ^ starts the match. \(? allows an optional opening parenthesis (escaped, because ( is a metacharacter). \d{3} is the three-digit area code. \)? allows an optional closing parenthesis. [-.\s]? permits a single optional separator — a hyphen, dot, or space (note the hyphen is safe at the start of a class). \d{3} then [-.\s]? then \d{4} match the rest of the number with another optional separator, and $ closes it off. The same pattern matches all four formats above.

Common pitfalls

Three traps catch nearly every beginner. First, forgetting to escape special characters: ., *, +, ?, (, ), [, {, ^, $, |, and \ all need a backslash when you mean them literally. A pattern like 3.14 will match 3x14 because the dot is a wildcard; you want 3\.14. Second, greedy surprises: as shown above, .* tends to grab more than you expect — reach for a lazy .*? or a more specific class. Third, flavor differences: regex is not one standard. JavaScript, PCRE, Python, and POSIX vary in features such as lookbehind, named groups, and Unicode support, so a pattern that works in one place may fail in another. Stick to the common core for portability and always test in the engine you will actually use.

Cheat sheet — the tokens you will use most: . any char · \d digit · \w word char · \s whitespace · [ ] class · [^ ] negated class · * 0+ · + 1+ · ? optional · {n,m} range · ^ start · $ end · \b word boundary · ( ) group · | or · add ? after a quantifier for lazy matching · escape specials with \.

The takeaway

Regex looks cryptic only because it is dense — every symbol earns its place. Learn the dozen tokens above, remember that metacharacters need escaping when taken literally, and watch out for greedy quantifiers, and you can read and write the patterns you will meet day to day. The fastest way to learn is to experiment: paste a pattern and some sample text into the regex tester and watch what lights up as you tweak it.

Frequently asked questions

What is a regular expression used for?
A regular expression is a compact pattern for describing text. It is used to search for matches inside strings, to validate input such as emails or phone numbers, and to find-and-replace across files. Most programming languages, text editors, and command-line tools support some form of regex.
What is the difference between a literal and a metacharacter?
A literal character matches itself — the pattern cat matches the letters c, a, t. A metacharacter has special meaning instead of matching itself; for example . means any character and + means one or more. To match a metacharacter literally you escape it with a backslash, so \. matches a real dot.
What does greedy versus lazy matching mean?
By default quantifiers are greedy: they match as much text as possible while still allowing the overall pattern to succeed. Adding a question mark makes them lazy, so they match as little as possible. For example, with the input <a><b>, the pattern <.*> matches the whole thing greedily, while <.*?> matches only <a>.
Why does the same regex behave differently in different tools?
Regex is not a single standard; it has flavors. JavaScript, PCRE (used by PHP and many tools), Python, and POSIX differ in features like lookbehind, named groups, and Unicode handling. Patterns that use only the common core — character classes, quantifiers, anchors, and groups — are the most portable. Always test in the flavor you will actually run.

This guide is general educational information. Regex behavior varies by language and tool — always verify a pattern in your target engine before relying on it.