← Back to Blog

Regex Cheatsheet: Common Patterns with Practical Examples

Why This Cheatsheet

Regular expressions (Regex) are the Swiss Army knife of text processing, but their dense syntax and many metacharacters mean even experienced developers frequently look things up. This guide curates the most commonly used validation patterns, each with explanations and caveats, so you can reuse them quickly rather than writing from scratch.

Syntax Quick Reference

Symbol Meaning Example
. Any single char (except newline) a.c → abc, a1c
* 0 or more of preceding ab* → a, ab, abbb
+ 1 or more of preceding ab+ → ab, abbb
? 0 or 1 of preceding colou?r → color, colour
{n} Exactly n times \d{4} → 2025
{n,m} n to m times \d{2,4} → 12, 123, 1234
^ Start of string ^Hello
$ End of string world$
[...] Character set [aeiou] matches any vowel
[^...] Negated set [^0-9] matches non-digits
\d Digit [0-9] \d+ → 123
\w Word char [A-Za-z0-9_] \w+ → hello_1
\s Whitespace space, tab, newline
(...) Capturing group Extract matched portion
(?:...) Non-capturing group Group without capture
(?=...) Positive lookahead Assert ahead without consuming
(?!...) Negative lookahead Assert ahead does not match

Common Validation Patterns

1. Email Address

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

Explanation:

  • ^[a-zA-Z0-9._%+-]+: local part, allows letters, digits, .%+-
  • @: required at sign
  • [a-zA-Z0-9.-]+: domain
  • \.[a-zA-Z]{2,}$: TLD, at least 2 letters

Note: This covers 99% of common emails but doesn't fully comply with RFC 5322 (the RFC rules are extremely complex and impractical to follow). In production, send a verification email rather than relying on a perfect regex.

2. Phone Number (China Mainland)

^1[3-9]\d{9}$

Explanation:

  • ^1: starts with 1
  • [3-9]: second digit 3-9 (covers all China Mobile/Unicom/Telecom prefixes)
  • \d{9}$: 9 more digits, 11 total

For international numbers, use Google's libphonenumber library instead of hand-written regex.

3. URL

^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$

Explanation:

  • https?: matches http or https
  • \/\/: protocol separator //
  • (www\.)?: optional www subdomain
  • Domain and TLD components, followed by path/query capture group

4. IPv4 Address

^(?:(?: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)$

Explanation:

  • 25[0-5]: matches 250-255
  • 2[0-4]\d: matches 200-249
  • 1\d\d: matches 100-199
  • [1-9]?\d: matches 0-99
  • One of the above matches a 0-255 octet, repeated 4 times separated by .

5. IPv6 Address (Simplified)

^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$

Note: IPv6 supports compressed form (::); full validation is very complex. Use a dedicated library in production.

6. Date (YYYY-MM-DD)

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

Explanation:

  • \d{4}: 4-digit year
  • (0[1-9]|1[0-2]): month 01-12
  • (0[1-9]|[12]\d|3[01]): day 01-31

Note: This does not validate month-day logic (e.g., Feb 30 passes). Use a date library for precise validation.

7. Hex Color

^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$

Matches 3- or 6-digit hex colors; # is optional.

8. Password Strength (8+ chars, upper + lower + digit)

^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$

Explanation:

  • (?=.*[a-z]): positive lookahead ensuring a lowercase letter
  • (?=.*[A-Z]): ensuring an uppercase letter
  • (?=.*\d): ensuring a digit
  • [A-Za-z\d@$!%*?&]{8,}: final match of 8+ allowed chars

9. China ID Card (18-digit)

^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$

Explanation: 6-digit region code, 4-digit year (19xx/20xx), month, day, 3-digit sequence, and check digit (possibly X).

10. Extract All Links

https?:\/\/[^\s]+

Quickly scrape all HTTP/HTTPS links from text.

Language Examples

JavaScript

const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const isValid = emailRegex.test('user@example.com'); // true
const matches = 'Visit https://example.com and https://oltools.com'
  .match(/https?:\/\/[^\s]+/g);

Python

import re
email_re = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
is_valid = bool(re.match(email_re, 'user@example.com'))
urls = re.findall(r'https?://[^\s]+', 'Visit https://example.com')

Java

Pattern emailPattern = Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
boolean isValid = emailPattern.matcher("user@example.com").matches();

Go

var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
isValid := emailRegex.MatchString("user@example.com")

Performance & Best Practices

  • Avoid catastrophic backtracking: nested quantifiers like (a+)+ cause exponential backtracking on certain inputs, freezing the engine
  • Pre-compile regex: compile once and reuse in loops
  • Use non-capturing groups: (?:...) instead of (...) when you don't need the capture, for better performance
  • Anchor boundaries: use ^/$ or \b appropriately to avoid unintended partial matches
  • Test edge cases: empty strings, very long inputs, Unicode characters
  • Don't parse HTML/XML with regex: use a proper parser; regex cannot handle nested structures

Recommended Debugging Tools

  • regex101.com: visual matching, per-symbol explanation, multi-engine support
  • regexr.com: interactive editing and reference
  • Official regex docs for each language
Advertisement

Frequently Asked Questions

What is the difference between greedy and lazy matching?

Quantifiers are **greedy** by default and match as much as possible. Against `<div>text</div>`, the pattern `<.*>` runs all the way to the final `>`; the lazy form `<.*?>` matches only `<div>`. Adding `?` after a quantifier switches it to lazy. Overusing greedy quantifiers is the number one reason a regex matches far more than intended. The [regex tester](/regex-tester.html) highlights the matched span so you can see it.

Why does my regex suddenly hang?

Almost certainly **catastrophic backtracking**. With nested quantifiers such as `(a+)+$`, a long non-matching input forces the engine to try an exponential number of combinations, pinning the CPU. Defend by avoiding nested quantifiers, using atomic groups or possessive quantifiers where the engine supports them, and capping input length. **Never run a regex without a timeout over user-controlled input** — that is a textbook ReDoS surface.

When should I use capturing versus non-capturing groups?

`(...)` is a capturing group: it stores the match in the results array, costing memory and consuming a numbered slot. `(?:...)` is non-capturing: it groups without saving. The rule is simple — **capture only what you intend to extract; use a non-capturing group for grouping or applying a quantifier**. Named groups `(?<name>...)` also read far better than positional indexes and survive refactoring without shifting.

How strict should an email or phone regex be?

**Loose is better than strict.** The full RFC 5322 email regex runs to thousands of characters and still cannot verify that a mailbox exists; phone number ranges change with regulation, so hardcoding them guarantees false rejections. A sane approach: use a permissive **format filter** (for email, check that there is an `@` with non-empty sides) to catch obvious typos, then verify for real by **sending a confirmation email or SMS code**. Let the regex reject the 99% of garbage, not prove correctness.

← Back to Blog