Regular expressions have a reputation for looking like line noise, and a lot of tutorials make it worse by front-loading every possible feature at once. In practice, you can get most of the everyday value from regex by learning about a dozen building blocks well. This is a tour of exactly those, with realistic examples rather than abstract syntax.
The building blocks that cover 90% of real use
| Pattern | Matches |
|---|---|
. | Any single character |
\d | Any digit (0โ9) |
\w | Any letter, digit, or underscore |
\s | Any whitespace (space, tab, newline) |
* | Zero or more of the previous thing |
+ | One or more of the previous thing |
? | Zero or one of the previous thing (optional) |
[abc] | Any one of a, b, or c |
^ / $ | Start / end of the line |
() | Groups a pattern, and captures the matched text |
Building up a real example: matching a simple email address
Rather than explaining each symbol in isolation, here's how they combine to solve a real problem. A (simplified) pattern for matching an email address:
^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$
Read left to right: ^ anchors to the start of the string. [\w.+-]+ matches one or more letters, digits, dots, plus signs, or hyphens โ the part before the @. @ matches a literal @ symbol. [\w-]+ matches the domain name. \. matches a literal dot (the backslash is needed because an unescaped dot means "any character"). [a-zA-Z]{2,} matches the extension โ at least two letters. $ anchors to the end of the string.
This is a deliberately simplified pattern โ real email validation is notoriously more complicated than it looks, and in practice it's usually better to do a loose format check like this one and then actually send a verification email, rather than trying to perfectly validate every technically legal address with regex alone.
Another common case: finding all the numbers in a string
\d+
Applied to "Order #4521 shipped 3 items", this matches "4521" and "3" as two separate matches โ \d matches a single digit, and + extends that to "one or more consecutive digits," so it correctly groups multi-digit numbers instead of matching each digit individually.
Greedy vs. lazy matching โ the concept that trips people up
By default, * and + are "greedy" โ they match as much as possible. Given the text <b>bold</b> and the pattern <.+>, a greedy match grabs the entire string from the first < to the very last >, not just <b>. Adding a ? after the quantifier (<.+?>) makes it "lazy" instead, matching as little as possible โ stopping at the first > it finds. This single distinction explains a large fraction of "why didn't my regex match what I expected" confusion.
How to actually practice this
Use an interactive regex tester (many free ones exist) that highlights matches in real time as you type โ this turns an abstract exercise into immediate visual feedback, which is a far faster way to build intuition than reading syntax reference tables alone. Start with real text you're actually trying to process โ log files, a CSV export, a list of filenames โ rather than made-up practice strings.
Regex syntax varies slightly between languages and tools (PCRE, JavaScript, Python's re module, and others) โ the patterns here use common conventions that work in most modern regex engines.