regexpatternsjavascript

Mastering Regular Expressions

· Cosyslabs

Regular expressions are a miniature language for describing text patterns. A regex engine tests a pattern against a string and reports matches, positions, and captured groups. Despite having a reputation for being cryptic, regex follows consistent rules — once you internalize the building blocks, you can construct patterns for nearly any text-matching problem.

The Building Blocks

Literal Characters and the Dot

cat      matches "cat" exactly
c.t      matches "cat", "cbt", "c9t", "c t" (. = any character except newline)
c\.t     matches "c.t" only (backslash escapes special meaning)

Character Classes

[aeiou]   matches any single vowel
[a-z]     matches any lowercase letter
[A-Z0-9]  matches any uppercase letter or digit
[^aeiou]  matches any character that is NOT a vowel (negated class)

Shorthand classes:

\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, etc.)
\S   non-whitespace

Quantifiers

a?      zero or one "a"
a*      zero or more "a"
a+      one or more "a"
a{3}    exactly 3 "a"s
a{2,4}  2 to 4 "a"s
a{2,}   2 or more "a"s

By default quantifiers are greedy — they match as much as possible. Add ? for lazy (minimal) matching:

<.*>    greedy: matches "<b>bold</b>" as one match
<.*?>   lazy: matches "<b>" then "</b>" as separate matches

Anchors

^pattern   matches at start of string (or line with multiline flag)
pattern$   matches at end of string
\bword\b   word boundary — matches "word" not inside "password"
\B         non-word boundary

Groups and Alternation

(cat|dog)      matches "cat" or "dog" (alternation in a group)
(?:cat|dog)    non-capturing group (no backreference)
(cat)(dog)     two capturing groups
\1             backreference to first group

Flags

// JavaScript flags
/pattern/g   — global (find all matches, not just first)
/pattern/i   — case-insensitive
/pattern/m   — multiline (^ and $ match line boundaries)
/pattern/s   — dotAll (. matches newline too)
/pattern/gi  — combine flags

JavaScript Regex API

const str = "The price is $42.50 or $18.00";

// test() — returns boolean
/\$[\d.]+/.test(str);  // true

// match() — first match (or all with /g)
str.match(/\$[\d.]+/);        // ["$42.50", ...]
str.match(/\$[\d.]+/g);       // ["$42.50", "$18.00"]

// exec() — iterate matches with groups
const re = /\$(\d+)\.(\d{2})/g;
let m;
while ((m = re.exec(str)) !== null) {
  console.log(`Dollars: ${m[1]}, Cents: ${m[2]}`);
}

// Named groups
const dateRe = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const { year, month, day } = "2026-05-20".match(dateRe).groups;

// replace() with regex
str.replace(/\$[\d.]+/g, "PRICE");          // "The price is PRICE or PRICE"
str.replace(/\$(\d+\.\d{2})/g, "USD $1");   // "The price is USD 42.50 or USD 18.00"

// split() with regex
"one  two\tthree".split(/\s+/);  // ["one", "two", "three"]

Practical Patterns

// Email (basic — full RFC 5322 is much more complex)
const email = /^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$/;

// URL
const url = /^https?:\/\/([\w\-]+\.)+[\w\-]+(\/[\w\-./?%&=#]*)?$/;

// IPv4 address
const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/;

// UUID v4
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

// ISO 8601 date
const isoDate = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?)?$/;

// Hex color
const hexColor = /^#([0-9a-fA-F]{3}){1,2}$/;

// Semantic version
const semver = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([\w.-]+))?(?:\+([\w.-]+))?$/;

// Digits only
const digitsOnly = /^\d+$/;

// Alphanumeric slug
const slug = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;

Lookaheads and Lookbehinds

Lookarounds match based on context without consuming characters:

// Positive lookahead: match "foo" only when followed by "bar"
/foo(?=bar)/

// Negative lookahead: match "foo" NOT followed by "bar"
/foo(?!bar)/

// Positive lookbehind: match "bar" preceded by "foo"
/(?<=foo)bar/

// Negative lookbehind: match "bar" NOT preceded by "foo"
/(?<!foo)bar/

// Example: find price numbers (only the digits, not the $ sign)
"$42.50".match(/(?<=\$)[\d.]+/);  // ["42.50"]

// Password strength: at least 8 chars, one uppercase, one digit
const strong = /^(?=.*[A-Z])(?=.*\d).{8,}$/;

Python and Go Regex

import re

# Python
pattern = re.compile(r'\b\d{4}-\d{2}-\d{2}\b')
matches = pattern.findall("Dates: 2026-05-20 and 2026-06-01")
# ['2026-05-20', '2026-06-01']

# Named groups
m = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})', '2026-05')
m.group('year')   # '2026'
m.group('month')  # '05'
// Go — regexp package (RE2 syntax, no lookaheads)
import "regexp"

re := regexp.MustCompile(`\b\d{4}-\d{2}-\d{2}\b`)
matches := re.FindAllString("Dates: 2026-05-20 and 2026-06-01", -1)

// Named groups
re2 := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})`)
match := re2.FindStringSubmatch("2026-05")
year := match[re2.SubexpIndex("year")]  // "2026"

Tools