DevKit Tools

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.

Try it as you readRegex TesterTest a regular expression against sample text, see every match highlighted with its capture groups, and preview a replacement.

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.
  • \d is a digit, \w is a word character (letter, digit or underscore), \s is 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.
text
\d{3}-\d{4}      matches 555-1234
colou?r          matches color and colour
\w+@\w+\.com      matches name@site.com

Anchors: 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.

text
\d+       finds 42 inside "abc42xyz"
^\d+$     only matches if the whole text is digits

Groups 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.

text
(\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 capturing

In 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
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

  • g global: find every match, not just the first.
  • i ignore case.
  • m multiline: ^ and $ match at the start and end of every line, not only of the whole text.
  • s dot-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.

Tools from this guide

More guides