Stop guessing at patterns — understand how regex actually works
A regular expression (regex or regexp) is a sequence of characters that defines a search pattern. You use regex to find text that matches a pattern, validate input formats, extract parts of a string, or replace matched text. Regex is supported in virtually every programming language and is one of the most powerful tools in a developer's toolkit.
The simplest regex is just a literal string. The pattern hello matches the exact text "hello" anywhere in a string.
A dot matches any single character except a newline. h.llo matches "hello", "hallo", "hxllo", etc.
[aeiou] matches any single vowel. [0-9] matches any digit. [a-zA-Z] matches any letter.
* — zero or more of the preceding element+ — one or more? — zero or one (makes it optional){3} — exactly 3{2,5} — between 2 and 5^ — matches the start of a string$ — matches the end of a string\b — matches a word boundary\d — any digit (same as [0-9])\w — any word character (letters, digits, underscore)\s — any whitespace character\D, \W, \S — the negated versionsFlags modify how the entire pattern is applied:
g — global: find all matches, not just the firsti — case-insensitive: hello matches "Hello", "HELLO", etc.m — multiline: ^ and $ match the start/end of each lines — dotAll: makes . also match newlines\b[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}\b
\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}
https?:\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:/~+#]*)
#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})\b
Parentheses () create a capture group — a portion of the match you can extract or reuse. Given the pattern (\d{4})-(\d{2})-(\d{2}) applied to a date like "2026-07-12", group 1 captures "2026", group 2 captures "07", and group 3 captures "12". This is how you pull structured pieces out of an otherwise unstructured string, and it's the basis of "find and replace with capture groups" — for example, reformatting YYYY-MM-DD to MM/DD/YYYY using a replacement pattern like $2/$3/$1.
Non-capturing groups, written (?:...), group parts of a pattern for the purposes of applying a quantifier without creating a numbered capture — useful when you need grouping but don't need to extract that piece.
By default, quantifiers are greedy — they match as much text as possible. Given the input <b>bold</b> and <b>more</b>, the pattern <b>.*</b> will greedily match from the very first <b> all the way to the very last </b>, swallowing both tags in one match. Adding a ? after the quantifier makes it lazy instead: <b>.*?</b> stops at the first </b> it finds, matching each tag pair separately. This distinction trips up beginners constantly and is worth testing explicitly whenever a match looks "too long."
example.com) needs to be written \., otherwise the unescaped . matches any character(a+)+b against a long string without a trailing "b") can cause exponential-time matching that appears to hang the programThe core syntax covered here (character classes, quantifiers, anchors, groups) is shared across JavaScript, Python, Java, and most other languages, since they all descend from the same Perl-compatible regular expression (PCRE) tradition. The differences tend to be small but important: JavaScript requires flags after the closing slash (/pattern/gi), Python uses a separate re module with flags passed as arguments, and named capture group syntax varies slightly between languages.
Why does my pattern match more text than I expected? Almost always a greedy quantifier — try making it lazy with a ?, or use a more specific character class instead of ..
How do I match a literal special character like a period or parenthesis? Escape it with a backslash: \., \(, \).
Should I use regex to validate email addresses? A simple pattern catches obvious typos, but a fully RFC-compliant email regex is extremely long and still won't confirm the address actually exists — pair regex validation with a confirmation email for anything that matters.
Use the DataBench Regex Tester to write and test patterns with live match highlighting, capture group display, and flag support. Runs entirely client-side.