HandyTools Hub

← All guides

Regex for Beginners: 10 Patterns You'll Actually Use Every Day

2026-08-06

Regular expressions have a reputation for being write-only code: a dense string of slashes, brackets, and braces that you copy from Stack Overflow and pray you never have to modify. But the core vocabulary is small — character classes, quantifiers, groups, and anchors — and once those click, the majority of real-world matching tasks boil down to short, readable patterns. This guide skips the exhaustive grammar tour and instead walks through the 10 patterns that come up most often in day-to-day work, explaining every symbol along the way, so you can write and adjust them yourself instead of only pasting them.

What a regular expression actually is

A regular expression (regex) is a tiny language for describing what a string looks like. Given a pattern, a regex engine answers two kinds of questions: does this text conform to the format (validation), and where are the parts that match the format (extraction). Every mainstream language ships with a regex engine, and editors, grep, and log-analysis tools all speak the same dialect.

A minimal example: \d{3}-\d{4} matches strings like 555-0134. Here \d means “any single digit,” {3} means “repeat the previous element three times,” and - is just a literal hyphen. Every regex, no matter how intimidating, is assembled from small parts like these — learn the parts and the whole becomes legible.

Pattern 1: Email address

^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$

Reading it piece by piece: ^ and $ are anchors meaning “start of string” and “end of string,” so the entire input must match, not just contain a match. [a-zA-Z0-9._%+-] is a character class — any one of the listed characters counts — and the + after it means “one or more times.” The @ is literal. In \., the backslash escapes the dot: an unescaped . means “any character,” so escaping pins it to an actual period. {2,} requires at least two letters for the top-level domain.

A fully RFC 5322-compliant email regex runs over a thousand characters and nobody uses it in practice. The pattern above covers essentially all real addresses you’ll see in a form. If you just want to check whether an address is well-formed without writing any code, an online email validator does the job instantly.

Pattern 2: URL

https?://[\w-]+(\.[\w-]+)+[\w\-.,@?^=%&:/~+#]*

In s?, the question mark makes the preceding character optional, so both http and https match. \w is shorthand for [a-zA-Z0-9_] — letters, digits, and underscore. The group (\.[\w-]+)+ matches dot-separated domain segments like .example.co, and the outer + requires at least one of them. The final character class lists characters commonly legal in paths and query strings, with * allowing zero or more of them.

This pattern is great for pulling URLs out of text. For strict validation, prefer your language’s built-in URL parser and treat the regex as a coarse filter.

Pattern 3: US-style phone number

^\+?1?[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$

This one reads almost like English once you know the parts: an optional +, an optional country code 1, an optional separator (-, ., or whitespace — note \s matches any whitespace), an optional opening parenthesis, exactly three digits, an optional closing parenthesis, another optional separator, three digits, another separator, and four digits. It accepts 415-555-0134, (415) 555-0134, and +1 415.555.0134 alike.

The deeper lesson: phone formats vary wildly by country, so there is no universal phone regex. For anything international, reach for a dedicated phone-number library instead of a pattern.

Pattern 4: Date in YYYY-MM-DD

^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$

This pattern showcases alternation inside a group. Parentheses bundle alternatives together and the pipe | means “or.” The month group (0[1-9]|1[0-2]) permits 01–09 and 10–12 while rejecting 00 and 13; the day group similarly allows 01–31.

Keep in mind this only enforces format, not existence — 2026-02-31 still matches even though February never has 31 days. Real date validation needs code. That’s the general rule: regex checks shape, programs check meaning.

Pattern 5: Extracting numbers (decimals and negatives)

-?\d+(\.\d+)?

The leading -? is an optional minus sign. \d+ grabs the integer part, and (\.\d+)? is an optional fractional part: an escaped dot followed by one or more digits, the whole group made optional by the trailing ?. It matches 42, -7, and 3.14.

This is one of the most-used patterns in log parsing and data cleaning. Combined with the global flag (g in JavaScript), it extracts every number from a blob of text in a single pass.

Pattern 6: Trimming whitespace

^\s+|\s+$

\s matches any whitespace character — spaces, tabs, newlines. Used with replacement (replace matches with an empty string), this pattern is a hand-rolled trim(): the two branches on either side of the | eat leading and trailing whitespace in one operation.

To also collapse runs of internal whitespace down to a single space, add a second pass replacing \s{2,} with one space — {2,} means “at least twice.”

Pattern 7: Quoted strings

"([^"\\]|\\.)*"

When parsing CSV or simple config formats, you often need the contents of double-quoted strings. The skeleton is "..."; the interesting part is the middle. [^"\\] is a negated character class matching anything that is neither a quote nor a backslash, while \\. matches escape sequences such as \" or \\. Alternating the two and repeating with * means an escaped quote inside the string won’t end the match early.

Pattern 8: IPv4 address

\b((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\b

It looks scary, but the structure is simple. The alternation (25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d) describes one valid 0–255 octet: 250–255, 200–249, 100–199, or anything below 100. Together those four cases cover exactly 0–255, so fakes like 999.1.1.1 are rejected. The first three octets are followed by a dot and repeated with {3}; the last stands alone. \b marks word boundaries so the pattern can’t match a slice out of a longer digit string.

For casual troubleshooting, the looser \b(?:\d{1,3}\.){3}\d{1,3}\b is usually fine — keep the strict version for validation logic.

Pattern 9: Matching non-ASCII text (Unicode ranges)

[\u4e00-\u9fff]+

Character classes aren’t limited to a-z. A class can span any range of code points, and this one — to , the main CJK unified ideograph block — matches runs of Chinese characters. Typical uses are validating that a name field contains Chinese, or extracting the Chinese portions from mixed-language text. The same technique generalizes: [\u0400-\u04ff]+ matches Cyrillic, [\u3040-\u30ff]+ matches Japanese kana. One gotcha worth knowing: punctuation from those scripts lives in different blocks, so a “remove Chinese commas and periods” pattern needs those characters listed separately. In modern JavaScript you can also reach for Unicode property escapes like \p{Script=Han} with the u flag, which reads better and stays correct as Unicode evolves.

Pattern 10: HTML tags

<([a-z][a-z0-9]*)\b[^>]*>(.*?)</\1>

The final pattern introduces two advanced pieces. [^>]* matches the attribute section (anything that isn’t >). (.*?) uses a lazy quantifier so it stops at the nearest closing tag instead of the farthest one. And \1 is a backreference — it requires the text captured by group 1 to appear again, guaranteeing the opening and closing tags have the same name: <div>...</div> matches, <div>...</span> doesn’t.

The standard caveat applies: regex is not a real HTML parser and will break on arbitrarily nested markup. But for grabbing simple, predictable fragments, it’s still the fastest tool in the box.

Greedy vs. lazy: the trap everyone hits once

Quantifiers (*, +, {m,n}) are greedy by default: they consume as much as they can. The classic demonstration is applying <.+> to <b>bold</b> plain <i>italic</i>. You expect it to match <b>; instead it swallows everything from the first < to the very last > on the line, because .+ eats as far as possible before the engine backtracks to find a final >.

Appending ? to a quantifier makes it lazy: match as little as possible. <.+?> yields four short, separate matches — <b>, </b>, <i>, </i>. The rule of thumb: when you’re matching “everything up to some terminator,” reach for a lazy quantifier or, often better, a negated class like [^>]*, which is typically faster and less error-prone.

How to test a regex properly

Shipping an untested regex is the classic beginner mistake. A better workflow:

  1. Iterate in a visual tester, not in your codebase. Paste real sample data into an online regex tester — it highlights every match live, lists capture groups, and flags invalid syntax, so each tweak gives instant feedback instead of another console.log cycle.
  2. Build positive and negative test sets. Positives should cover every legitimate variation you expect (emails with + tags, URLs with ports). Negatives matter just as much: an email missing its @, the date 2026-13-45, the IP 300.1.1.1 — near-misses like these must fail to match.
  3. Watch your flags. The same pattern behaves completely differently with g (global), i (case-insensitive), or m (multiline, where ^ and $ match per line). Make sure the flags in your tester match the ones in production code.
  4. Beware catastrophic backtracking. Nested quantifiers like (a+)+ can take exponential time on adversarial input and hang a service. If your pattern repeats a repetition, benchmark it against a long non-matching string before deploying.

Nobody memorizes every symbol, and you don’t need to — keep a regex cheat sheet open while you work. Regex is a craft you learn by doing: get comfortable with these ten patterns, and new requirements become small variations on things you already know rather than trips back to a grammar textbook.