Regex basics — patterns, groups, quantifiers, boundaries, and safe matching
What regex does
A regular expression describes a text-matching rule
A regular expression, usually shortened to regex, is a pattern interpreted by a regex engine. Applications use patterns to search, extract, split, replace, and validate text. The engine scans candidate positions and attempts to satisfy the pattern according to its matching rules.
Regex syntax is not perfectly universal. JavaScript, Python, Java, .NET, PCRE, POSIX tools, and editors differ in supported groups, lookbehind, flags, escaping, and replacement syntax. Identify the language and engine before copying a pattern from another environment.
Literals and metacharacters
Most characters match themselves; metacharacters control the pattern
Letters and digits usually match the same characters in the input. Characters such as ., *, +, ?, parentheses, brackets, braces, anchors, and the backslash have structural meaning. Escape a metacharacter when it must be matched literally.
There may be two escaping layers: the programming-language string and the regex engine. A JavaScript regex literal /\d+/ contains one backslash, while the equivalent string passed to new RegExp needs "\\d+" so the engine receives that same backslash.
# Pattern: match a literal dot followed by digits
\.\d+
# Matches
.42
.2026
Character classes
Classes match one character from a defined set
[abc] matches one of three characters, while [^abc] matches one character outside that set. Ranges such as [A-Z] depend on character ordering and usually cover only ASCII. Shorthands such as \d, \w, and \s can have Unicode-dependent behavior across engines.
Use explicit classes when the accepted alphabet is part of a data contract. For international text, check whether the engine supports Unicode properties such as \p{L} and whether the required Unicode flag is enabled.
^[A-Z]{2}-[0-9]{4}$
# Matches an ASCII code such as:
AB-2048
Quantifiers
Quantifiers repeat the preceding token or group
* means zero or more, + means one or more, and ? means zero or one. Braces express exact or bounded counts. Quantifiers are greedy by default in many engines: they initially consume as much as possible and backtrack when the rest of the pattern cannot match.
Place repeated structure inside a group. ab+ repeats only b, whereas (?:ab)+ repeats the two-character sequence. Use a noncapturing group when the grouping is structural and the substring does not need to be returned.
a{3}matches exactly three consecutiveacharacters.a{2,4}matches between two and four.a+?is a lazy form in engines that support lazy quantifiers.(?:https?://)?makes an entire protocol group optional.
Groups and captures
Capturing groups return meaningful parts of a match
Parentheses group pattern operations and, by default, capture the matched substring. Numbered captures are referenced by position, which becomes fragile when groups are inserted earlier in a pattern. Named groups make extraction and replacement intent clearer when the engine supports them.
^(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})$
# Input
2026-07-19
# Captures
year=2026 month=07 day=19
Anchors and boundaries
Anchors assert a position instead of consuming text
^ and $ commonly represent the start and end of an input, but multiline mode may make them operate at line boundaries. Some engines provide absolute input anchors with different names. A word boundary \b asserts a transition between word and non-word characters and inherits the engine definition of a word character.
Validation patterns normally need anchors around the entire allowed format. Without anchors, a pattern can succeed because one valid-looking substring exists inside otherwise invalid input.
# Search: finds a date inside longer text
[0-9]{4}-[0-9]{2}-[0-9]{2}
# Shape validation: requires the entire input to have that form
^[0-9]{4}-[0-9]{2}-[0-9]{2}$
Flags and matching mode
Flags change how the same pattern is interpreted
Common flags enable case-insensitive matching, global iteration, multiline anchors, dot matching across line breaks, or Unicode-aware behavior. A global flag may also change API state or return shape. Read the host-language API documentation instead of treating flags as portable suffixes.
i: case-insensitive matching, with engine-specific Unicode rules.g: find all matches in JavaScript-style APIs rather than stopping at the first.m: make line anchors operate across multiple lines in many engines.s: allow dot to match line terminators in engines with dot-all mode.
Replacement
Replacement syntax is a separate language
A replacement usually refers to captured groups, but the notation differs: environments may use $1, \1, or named forms. Test the replacement API as well as the matching pattern. Escaping rules for a shell, JSON file, programming-language string, and replacement template can all apply at once.
# Pattern
^([^,]+),\s*([^,]+)$
# Input
Lovelace, Ada
# Replacement in a $1-style API
$2 $1
Testing and safety
Test examples, near misses, and long adversarial input
A useful regex test set includes expected matches, expected failures, empty input, boundary lengths, Unicode text, and strings that almost match. Regex validates textual shape, not real-world meaning: a date-shaped string can still name an impossible calendar date, and an email-like string does not prove the mailbox exists.
Ambiguous nested repetition can cause excessive backtracking and poor performance. Prefer bounded input, specific character classes, and unambiguous structure. Avoid patterns such as nested broad quantifiers when matching untrusted long text, and measure worst-case behavior in the actual engine.
- Write down the accepted and rejected examples before making the pattern more compact.
- Anchor the pattern when validating a complete field.
- Use ordinary string operations when the task is a fixed prefix, suffix, or delimiter.
- Keep semantic validation in code when it is clearer than a large pattern.
Related