Almost every regular expression is assembled from two ideas: what kind of character to match, and how many times to match it. Character classes answer the first question; quantifiers answer the second. Once those click, the rest is mostly punctuation.
Character classes: what to match
The shorthand classes cover the cases you reach for constantly. \d is any digit, \w is any word character (a letter, digit, or underscore), and \s is any whitespace. Each has an uppercase negation: \D is any non-digit, \W any non-word character, \S any non-whitespace. A bare dot . matches any character at all — except a line break, unless you set the s (dotAll) flag.
When the shorthands are too broad, build your own class with square brackets. [aeiou] matches one vowel; [a-z] matches one lowercase letter using a range; [A-Za-z0-9] combines several ranges. Put a caret first to invert it: [^0-9] matches any single character that is not a digit. Inside a class most metacharacters lose their special meaning, so [.+*] matches a literal dot, plus, or asterisk — no escaping needed.
A class always matches exactly one character. To match more than one, you need a quantifier.
Quantifiers: how many
The three short quantifiers do most of the work. * means zero or more, + means one or more, and ? means optional — zero or one. So \d* matches an empty string or any run of digits, while \d+ insists on at least one digit.
When you need a specific count, use braces. \d{4} matches exactly four digits, \d{2,} matches two or more, and \d{2,4} matches between two and four. A US-style phone fragment might be \d{3}-\d{4}, and a simple email shape is \w+@\w+\.\w+ — note the \. there, an escaped dot that matches a literal period rather than "any character".
Greedy versus lazy
By default quantifiers are greedy: they grab as much as they can and only give characters back if the rest of the pattern fails. Add a trailing ? to make a quantifier lazy, so it grabs as little as possible. The difference matters the moment you try to match delimited content. Against the text [one][two], the greedy \[.+\] matches the whole string in one go, because .+ swallows everything up to the final bracket. The lazy \[.+?\] stops at the first closing bracket and matches just [one]. Choosing greedy versus lazy is one of the most common fixes when a pattern "matches too much".
Anchors, briefly
Anchors match a position rather than a character. ^ is the start of the string (or the start of a line with the m flag), $ is the end, and \b is a word boundary — the seam between a word character and a non-word character. Wrapping a pattern in ^...$ forces it to match the entire input, which is exactly what you want when validating a whole field rather than finding a fragment inside it.
Paste any of these into the Regex Toolkit to watch the matches light up as you type, then read on about groups and backreferences to capture and reuse the parts you match.