Regex Basics: Read and Write Regular Expressions Without Fear
3 min read4 October 2026By Muhammad Abdullah
Regular expressions look like line noise until you notice they are built from a small number of pieces. Learn about ten of them and you can read most patterns you meet and write the ones you need. The rest is practice, ideally with a tester that shows every match as you type.
Literal characters and the special ones
Most characters match themselves: the pattern cat finds the text cat. A few characters have special meanings, . * + ? ^ $ ( ) [ ] { } | \, and to match one of those literally you put a backslash before it. To find a full stop, write \..
Character classes: one character from a set
[abc]matches a, b or c.[a-z]matches any lowercase letter.[^0-9]matches anything that is not a digit.\dis a digit,\wis a word character (letter, digit or underscore),\sis whitespace. Capitals mean the opposite:\D,\W,\S..matches any character except a newline.
Quantifiers: how many
*zero or more,+one or more,?zero or one.{3}exactly three,{2,4}between two and four,{2,}two or more.
\d{3}-\d{4} matches 555-1234
colou?r matches color and colour
\w+@\w+\.com matches name@site.comAnchors: where, not what
^ matches the start and $ the end. \b matches a word boundary. Without anchors, a pattern can match anywhere inside the text, which is the cause of many validation bugs.
\d+ finds 42 inside "abc42xyz"
^\d+$ only matches if the whole text is digitsGroups and alternatives
Parentheses group part of a pattern so a quantifier applies to all of it, and capture what it matched so you can use it later. | means or.
(\d{4})-(\d{2})-(\d{2}) captures year, month and day from 2026-10-04
(jpg|png|webp)$ matches a file ending in any of the three
(?:ab)+ groups without capturingIn a replace, $1, $2 and so on refer to the captured groups. Replacing (\d{4})-(\d{2})-(\d{2}) with $3/$2/$1 turns 2026-10-04 into 04/10/2026.
Greedy and lazy
Quantifiers are greedy: they take as much as they can. That surprises people when there are several possible matches.
text: <b>one</b> and <b>two</b>
<b>.*</b> matches the whole line, from the first <b> to the last </b>
<b>.*?</b> matches <b>one</b>, then <b>two</b>Adding ? after a quantifier makes it lazy, so it takes as little as possible. When a match runs further than you expected, a greedy quantifier is the usual reason.
Flags
gglobal: find every match, not just the first.iignore case.mmultiline:^and$match at the start and end of every line, not only of the whole text.sdot-all:.matches newlines too.
Know when to stop
Regex is excellent for finding patterns in text and poor at parsing nested structures. Use a real parser for HTML and JSON. And be wary of nested quantifiers like (a+)+: on certain inputs they can take an extremely long time, which has taken real websites down.
Common questions
What does .* mean in regex?
Any character, zero or more times. It is greedy, so it matches as much as possible; use .*? to match as little as possible.
How do I match a literal dot or bracket?
Put a backslash before it: \. for a dot, \( for a bracket. Inside square brackets, most special characters lose their meaning.
Why does my regex match more than I expected?
Usually a greedy quantifier or a missing anchor. Try the lazy form .*? and add ^ and $ when the whole text must match.