CODE: Regular Expressions — A Complete Practical Guide

Regular expressions (regex) are a universal language for pattern matching and text processing. From validating email addresses to extracting data from logs, regex is one of the highest-leverage tools in any developer's toolkit. This guide covers everything from basic literal matching through quantifiers, groups, anchors, and real-world patterns.

Regular expressions appear everywhere in software development: in search tools like grep and ripgrep, in validation logic for web forms, in text editors, in data pipelines, and in every major programming language. A developer who understands regex can accomplish in one line what would otherwise require dozens of lines of string manipulation code.


What Is a Regular Expression?

A regular expression (regex or regexp) is a sequence of characters that defines a search pattern. This pattern can be applied to text to:

  • Match: Does this text contain this pattern?
  • Search: Find all locations where the pattern occurs
  • Extract: Capture specific parts of matching text
  • Validate: Does this entire string conform to the pattern?
  • Replace: Substitute matched text with something else

text
Regex Core Concept:
Pattern:  /\d{4}-\d{2}-\d{2}/
Text:     "Event date: 2025-06-01, Published: 2025-01-15"
Matches:  "2025-06-01" and "2025-01-15"

Regex is supported in virtually every programming language and most command-line tools, making it a universal skill that transfers across contexts.


Literal Characters

The simplest regex matches exact characters:

text
Pattern: /cat/
Matches: "I have a cat at home"
         ^^^

Regex is case-sensitive by default: /cat/ matches "cat" but not "Cat" or "CAT" (unless case-insensitive mode is enabled with the i flag).


Metacharacters

Metacharacters are characters with special meaning in regex. They must be escaped with \ to be treated as literal characters:

MetacharacterMeaning
.Any single character (except newline)
^Start of line
$End of line
\Escape the next character
*Zero or more of the preceding
+One or more of the preceding
?Zero or one of the preceding
{ }Quantifier boundaries
[ ]Character class
( )Grouping
`\`Alternation (OR)

text
Pattern: /c.t/  (. matches any character)
Matches: "cat", "cot", "cut", "c3t", "c!t"

Pattern: /c\.t/  (\. matches literal dot)
Matches: "c.t"
Does not match: "cat", "cot"


Character Classes

Character classes match any one character from a specified set:

text
[abc]    — match 'a', 'b', or 'c'
[^abc]   — match any character EXCEPT 'a', 'b', or 'c'
[a-z]    — match any lowercase letter
[A-Z]    — match any uppercase letter
[0-9]    — match any digit
[a-zA-Z] — match any letter (upper or lower)
[a-zA-Z0-9_] — match any word character

text
Pattern: /[aeiou]/
Matches: any single vowel

Pattern: /[^0-9]/
Matches: any character that is NOT a digit

Predefined Character Classes

These shorthand classes are available in most regex flavors:

ClassEquivalentMeaning
\d[0-9]Any digit
\D[^0-9]Any non-digit
\w[a-zA-Z0-9_]Any word character
\W[^a-zA-Z0-9_]Any non-word character
\s[ \t\n\r\f\v]Any whitespace
\S[^ \t\n\r\f\v]Any non-whitespace

text
Pattern: /\d\d\d/
Matches: any three consecutive digits
Example: "abc123def" → matches "123"

Pattern: /\w+/
Matches: one or more word characters
Example: "hello world" → matches "hello" and "world"


Quantifiers

Quantifiers specify how many times a character or group should repeat:

QuantifierMeaning
*0 or more times
+1 or more times
?0 or 1 time (optional)
{n}Exactly n times
{n,}At least n times
{n,m}Between n and m times

text
Pattern: /a{2,4}/
Matches: "aa", "aaa", "aaaa"
Does not match: "a" (only 1), "aaaaa" (5, too many)

Pattern: /colou?r/
Matches: "color" and "colour"
(the 'u' is optional)

Pattern: /\d+/
Matches: one or more digits: "1", "42", "1000"

Pattern: /\d*/
Matches: zero or more digits (can match empty string)

Greedy vs. Lazy Quantifiers

By default, quantifiers are greedy — they match as much as possible:

text
Pattern: /<.+>/      (greedy)
Text:    "<b>hello</b>"
Match:   "<b>hello</b>"  (matches everything)

Pattern: /<.+?>/     (lazy, with ?)
Text:    "<b>hello</b>"
Matches: "<b>" and "</b>" (matches as little as possible)

Add ? after any quantifier to make it lazy: *?, +?, ??, {n,m}?.


Anchors and Boundaries

Anchors match a position in the string, not a character:

AnchorPosition
^Start of line/string
$End of line/string
\bWord boundary
\BNon-word boundary

text
Pattern: /^hello/
Matches: "hello world" (starts with "hello")
Does not match: "say hello" (hello not at start)

Pattern: /world$/
Matches: "hello world" (ends with "world")
Does not match: "world peace" (world not at end)

Pattern: /\bword\b/
Matches: "word" as a whole word
Does not match: "sword" or "words" (word is part of a larger word)


Grouping and Capturing

Parentheses create groups that:

  1. Treat multiple characters as a unit
  2. Capture the matched text for later use (back-references and extraction)

text
Pattern: /(ab)+/
Matches: "ab", "abab", "ababab"

Pattern: /(cat|dog)/
Matches: "cat" or "dog"

Capturing Groups

Captured groups are numbered starting at 1, left to right. They can be referenced with \1, \2, etc.:

text
Pattern: /(\w+)\s\1/
Purpose: find repeated words
Text:    "the the quick brown fox"
Match:   "the the"  (word followed by space followed by same word)

Non-Capturing Groups

When you need grouping but not capture:

text
Pattern: /(?:cat|dog)s?/
Matches: "cat", "cats", "dog", "dogs"
The ?: prevents capturing the group


Common Real-World Patterns

Email Address

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

Breakdown:
[a-zA-Z0-9._%+-]+  — local part (one or more chars)
@                   — literal @ symbol
[a-zA-Z0-9.-]+     — domain name
\.                  — literal dot
[a-zA-Z]{2,}       — TLD (at least 2 letters)

Matches: user@example.com, my.name+tag@domain.co.uk

US Phone Number

text
/\(\d{3}\) \d{3}-\d{4}/

Breakdown:
\(       — literal opening parenthesis
\d{3}    — 3 digits (area code)
\)       — literal closing parenthesis
\s       — space
\d{3}    — 3 digits
-        — literal hyphen
\d{4}    — 4 digits

Matches: (123) 456-7890

URL

text
/https?:\/\/(www\.)?[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/

Breakdown:
https?   — "http" or "https"
:\/\/    — "://" (forward slashes escaped)
(www\.)? — optional "www."
[a-zA-Z0-9.-]+ — domain
\.[a-zA-Z]{2,} — TLD

Matches: https://www.example.com, http://subdomain.site.org

Date (YYYY-MM-DD)

text
/\d{4}-\d{2}-\d{2}/

Matches: 2025-06-01, 1999-12-31

IPv4 Address

text
/\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/

Matches: 192.168.1.1, 10.0.0.255
Does not match: 999.999.999.999


Regex in Python

python
import re

# Find all email addresses in text
text = "Contact us at support@example.com or sales@example.com."
pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"

emails = re.findall(pattern, text)
print("Emails found:", emails)
# Emails found: ['support@example.com', 'sales@example.com']

# Validate a phone number
phone = "(555) 867-5309"
if re.match(r"^\(\d{3}\) \d{3}-\d{4}$", phone):
    print("Valid phone number")

# Extract date components
date_str = "Today is 2025-06-01"
match = re.search(r"(\d{4})-(\d{2})-(\d{2})", date_str)
if match:
    year, month, day = match.groups()
    print(f"Year: {year}, Month: {month}, Day: {day}")

# Replace matched text
result = re.sub(r"\b\d{4}\b", "XXXX", "My card: 1234 expires 2025")
print(result)  # My card: XXXX expires XXXX


Regex in Command-Line Tools

bash
# grep: find lines matching a pattern
grep "\d{3}-\d{4}" phone_numbers.txt

# grep with extended regex (supports +, ?, |)
grep -E "^(ERROR|WARN)" application.log

# grep with Perl-compatible regex
grep -P "\d{4}-\d{2}-\d{2}" log.txt

# sed: find and replace
sed 's/old_pattern/new_text/g' file.txt

# Extract IP addresses from a log
grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" access.log


Performance Considerations

Regex can be slow when misused:

text
Catastrophic Backtracking:
Pattern: /^(a+)+$/
Text:    "aaaaaaaaaaaaaaaaaab"

This pattern can take exponential time to fail because
the engine tries every possible combination of the inner
and outer + quantifiers. Known as "ReDoS" (Regex DoS).

Guidelines for efficient regex:

  • Prefer specific character classes over .
  • Avoid nested quantifiers on overlapping patterns
  • Use anchors ^ and $ when matching whole strings
  • Precompile regex patterns when used in loops
  • Prefer possessive quantifiers or atomic groups for backtracking-prone patterns

Summary: Regex Quick Reference

text
+------------------+-------------------------------+
| Pattern          | Matches                       |
+------------------+-------------------------------+
| abc              | Literal "abc"                 |
| .                | Any character (not newline)   |
| \d               | Digit [0-9]                   |
| \w               | Word char [a-zA-Z0-9_]        |
| \s               | Whitespace                    |
| [abc]            | 'a', 'b', or 'c'             |
| [^abc]           | Not 'a', 'b', or 'c'         |
| [a-z]            | Lowercase letter              |
| ^                | Start of line                 |
| $                | End of line                   |
| \b               | Word boundary                 |
| *                | 0 or more                     |
| +                | 1 or more                     |
| ?                | 0 or 1                        |
| {n,m}            | Between n and m               |
| (abc)            | Capture group                 |
| (?:abc)          | Non-capturing group           |
| a|b              | 'a' or 'b'                   |
| \1               | Back-reference to group 1     |
+------------------+-------------------------------+


Conclusion

Regular expressions are one of the highest-leverage skills in software development. A single well-crafted regex replaces complex parsing loops, handles edge cases automatically, and works across languages and tools.

The learning curve is real — the syntax is dense and failures can be cryptic. But the investment pays off quickly. Start with simple patterns, build up complexity gradually, and use an online tool like regex101.com to visualize matches and test patterns interactively as you learn.

Once you are comfortable with regex, text processing tasks that once felt tedious become one-liners.