A regular expression (regex) is a compact way to describe a text pattern: not a specific string, but a rule a string must match. Instead of looping character by character, you can check for or find a complex structure with a single expression.
Basic building blocks
- Character classes —
\d(digit),\w(letter/digit/underscore),\s(whitespace), or a custom class like[a-zA-Z]. - Quantifiers —
*(0 or more),+(1 or more),?(0 or 1),{2,5}(2 to 5 times). - Capture groups — parentheses
(...)mark a part of the match that can be used separately later, for instance in a replacement. - Anchors —
^and$tie a pattern to the start or end of a line.
One name, several incompatible dialects
"Regex" isn't one language — it's a family. Python's re module, PCRE (used by PHP and, historically, by many other tools), POSIX basic/extended regex (used by grep and sed), and JavaScript's native RegExp all diverge on real syntax details: named groups are written (?P<name>...) in Python and older PCRE but (?<name>...) in JavaScript and modern PCRE; lookbehind ((?<=...)) only became fully supported in JavaScript with ES2018, years after Python and PCRE already had it. A pattern copied from a Python script into a JavaScript project can fail to compile for reasons that have nothing to do with the pattern's logic.
Greedy vs lazy matching
By default, quantifiers are "greedy" — they try to consume as many characters as possible, backing off only if that prevents a match. For example, <.+> on <a>text</a> would capture everything from the first < to the very last >. Adding ? after a quantifier (<.+?>) makes it "lazy" — it consumes as little as possible, stopping at the first valid match.
Why you need this
- Validating the format of input data (email, phone number, postal code).
- Finding and bulk-replacing text based on a pattern rather than an exact match.
- Extracting structured data from logs or unstructured text.
Catastrophic backtracking
Nested quantifiers like (a+)+ can, on certain inputs, force a regex engine to try an exponential number of combinations before concluding there's no match — the page or server "freezes" on what looks like a simple expression. This is a real vulnerability class (ReDoS), so complex nested quantifiers should be stress-tested against long "near-match" strings, not just the expected examples.