EDT
Everyday Dev Tools
Fast tools, no fuss
Home / Tutorials / Practical Regex Examples for Everyday Development Tasks
Regex

Practical Regex Examples for Everyday Development Tasks

2026-06-21 · 21 min read

Useful regex patterns for log parsing, validation, text processing, and more with detailed explanations and real-world use cases.

How to Use These Examples

These are regex patterns I actually use in my daily work as a developer. I've collected them over years of log parsing, form validation, and text wrangling.

A few things to remember:

- Copy these into our Regex Tester to experiment with them

- None of these are perfect for every use case — adapt them to your specific needs

- Always test with both valid and invalid inputs

- Consider edge cases (empty strings, weird formatting, etc.)

- If a pattern gets too complicated, ask yourself if regex is even the right tool

Let's dive in with practical patterns you can use today.

Pattern 1: Match Dates in YYYY-MM-DD Format

Dates are one of the most common things to validate or extract. This pattern checks for the basic format of ISO 8601 dates.

**Basic pattern (format check only):** `^\d{4}-\d{2}-\d{2}$`

**What it matches:**

- ✅ "2026-06-20"

- ✅ "2023-12-31"

- ✅ "9999-99-99" (yes, this matches — we're just checking format!)

**What it doesn't match:**

- ❌ "20/06/2026" (wrong order)

- ❌ "2026/06/20" (wrong separator)

- ❌ "2026-6-20" (missing leading zeros)

**Breakdown:**

- `^` — Start of string

- `\d{4}` — Exactly 4 digits for the year

- `-` — Literal dash separator

- `\d{2}` — Exactly 2 digits for the month

- `-` — Literal dash separator

- `\d{2}` — Exactly 2 digits for the day

- `$` — End of string

**Important note:** This doesn't validate that the date is actually valid — it will happily match "2026-02-31" (February 31st doesn't exist!) or "2026-13-01" (month 13 doesn't exist). It just checks the format.

**More precise version (validates month ranges):** `^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$`

This improved version:

- `(0[1-9]|1[0-2])` — Month must be 01-12

- `(0[1-9]|[12]\d|3[01])` — Day must be 01-31

It still won't catch February having only 28/29 days, or April having only 30 days, but it's better than just checking digits!

**In JavaScript:**

function isValidDateFormat(dateStr) {
  const regex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
  return regex.test(dateStr);
}

**Real-world use case:** I use this for quick validation before passing the date to a proper date library (like date-fns or luxon) for full validation. Regex can do the format check, but actual date logic should be left to date libraries.

Pattern 2: Email Address Validation (Practical, Not Perfect)

Email validation is a classic regex problem. But here's the truth: a 100% RFC-compliant email regex is enormous and unreadable. For most applications, a practical pattern that catches common typos is better.

**My go-to practical pattern:** `^[^\s@]+@[^\s@]+\.[^\s@]+$`

**What it matches:**

- ✅ "[email protected]"

- ✅ "[email protected]"

- ✅ "[email protected]"

**What it doesn't match (and these are good rejections):**

- ❌ "user@" (missing domain)

- ❌ "@example.com" (missing username)

- ❌ "[email protected]" (empty domain part)

- ❌ "user example.com" (missing @)

**Breakdown:**

- `^` — Start of string

- `[^\s@]+` — One or more characters that are NOT whitespace or @ (the username part)

- `@` — Literal @ symbol

- `[^\s@]+` — One or more characters that are NOT whitespace or @ (the domain part before the dot)

- `\.` — Literal dot

- `[^\s@]+` — One or more characters that are NOT whitespace or @ (the TLD part)

- `$` — End of string

**Is this perfect?** No. It will reject some technically valid but unusual emails (like those with quotes or spaces, which are allowed in RFC but rare in practice). And it will accept some invalid ones (like "[email protected]" — wait, no, actually this pattern won't, because the `[^\s@]+` after the dot requires at least one character).

**My philosophy on email validation:** Don't try to be too clever. The best validation is to send a confirmation email — if the user receives it, it's valid!

**In JavaScript:**

function looksLikeEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

I use this pattern to catch obvious typos before trying to send an email — it saves on bounced emails from things like "user@example" or "userexample.com".

Pattern 3: Extract IP Addresses from Log Files

This is one of my most-used patterns for debugging server issues. I constantly need to extract IP addresses from access logs, error logs, and more.

**Pattern for IPv4 addresses:** `\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b`

**What it finds in log lines:**

2026-06-20 10:30:00 INFO Request from 192.168.1.100 to /api/users
2026-06-20 10:30:05 WARN Rate limit hit for 10.0.0.5
2026-06-20 10:30:10 ERROR Error for 172.16.0.25: Timeout

It will extract: "192.168.1.100", "10.0.0.5", "172.16.0.25".

**Breakdown:**

- `\b` — Word boundary (prevents partial matches like "x192.168.1.100" or "192.168.1.100x")

- `\d{1,3}` — 1 to 3 digits (each octet)

- `\.` — Literal dot

- `\b` — Word boundary at the end

**More precise version (validates octet ranges 0-255):** `\b(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\b`

This more complex pattern ensures each octet is between 0 and 255 — it won't match "999.999.999.999" like the simpler version will.

**In JavaScript:**

function extractIPs(logText) {
  const regex = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g;
  return logText.match(regex) || [];
}

**Real-world use case:** I once had a server getting a suspicious amount of traffic. I used this pattern to extract all the IPs from the access logs, counted them, and found that 80% of the traffic was coming from just 10 IPs — we added those to a blocklist and the problem went away.

**Note:** This is for IPv4 only. IPv6 is more complex and usually less common in logs unless you're specifically dealing with IPv6 traffic.

Pattern 4: Hex Color Codes

Hex colors are everywhere in CSS and design work. This pattern matches both 3-digit and 6-digit hex colors.

**Pattern:** `^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$`

**Or, with the `i` flag (case-insensitive):** `^#([0-9a-f]{3}|[0-9a-f]{6})$`

**What it matches:**

- ✅ "#ff0000" (6-digit, lowercase)

- ✅ "#FF00FF" (6-digit, uppercase)

- ✅ "#f00" (3-digit, lowercase)

- ✅ "#ABC" (3-digit, uppercase)

- ✅ "#123abc" (mix of letters and numbers)

**What it doesn't match:**

- ❌ "#ff00" (wrong length — 4 digits)

- ❌ "#ff000" (wrong length — 5 digits)

- ❌ "ff0000" (missing #)

- ❌ "#ffxx00" (invalid characters)

**Breakdown:**

- `^` — Start of string

- `#` — Literal hash symbol

- `(...)` — Group with two options

- `[0-9a-fA-F]{3}` — 3 hex characters

- `|` — OR

- `[0-9a-fA-F]{6}` — 6 hex characters

- `$` — End of string

**In JavaScript:**

function isValidHexColor(color) {
  const regex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
  return regex.test(color);
}

**Bonus pattern for RGB/RGBA:** If you also need to match RGB/A colors, try: `^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(0|1|0?\.\d+))?\s*\)$`

That matches things like "rgb(255, 0, 0)" or "rgba(255, 0, 0, 0.5)".

**Real-world use case:** I used this in a design tool to validate user-entered colors before converting them to other formats (HSL, RGB, etc.).

Pattern 5: 24-Hour Time (HH:MM)

Validating time formats is another common task. This pattern checks for valid 24-hour time in HH:MM format.

**Pattern:** `^([01]\d|2[0-3]):[0-5]\d$`

**What it matches:**

- ✅ "00:00" (midnight)

- ✅ "09:30" (9:30 AM)

- ✅ "12:00" (noon)

- ✅ "14:00" (2 PM)

- ✅ "23:59" (one minute before midnight)

**What it doesn't match:**

- ❌ "24:00" (invalid — 23:59 is the max)

- ❌ "9:30" (missing leading zero on hour)

- ❌ "09:60" (invalid minutes)

- ❌ "09-30" (wrong separator)

**Breakdown:**

- `^` — Start of string

- `([01]\d|2[0-3])` — Hour: either 00-19, or 20-23

- `:` — Literal colon

- `[0-5]\d` — Minutes: 00-59

- `$` — End of string

**Hour breakdown:**

- `[01]\d` — Hours starting with 0 or 1 (00-19)

- `|` — OR

- `2[0-3]` — Hours starting with 2, followed by 0-3 (20-23)

**In JavaScript:**

function isValid24HourTime(time) {
  const regex = /^([01]\d|2[0-3]):[0-5]\d$/;
  return regex.test(time);
}

**With optional seconds:** If you want to allow optional seconds, use: `^([01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$` — this matches "09:30" or "09:30:45".

**Real-world use case:** I used this in a scheduling app to validate the time part of events before storing them.

Pattern 6: US Zip Codes (5-digit and ZIP+4)

US zip codes are either 5 digits, or 5 digits plus a hyphen plus 4 more digits (ZIP+4).

**Pattern:** `^\d{5}(-\d{4})?$`

**What it matches:**

- ✅ "12345" (5-digit)

- ✅ "12345-6789" (ZIP+4)

**What it doesn't match:**

- ❌ "1234" (too short)

- ❌ "123456" (too long)

- ❌ "1234-56789" (wrong grouping)

- ❌ "12345_6789" (wrong separator)

**Breakdown:**

- `^` — Start of string

- `\d{5}` — Exactly 5 digits

- `(-\d{4})?` — Optional group: hyphen followed by exactly 4 digits

- `$` — End of string

**The `?` makes the group optional.** That's how we support both 5-digit and ZIP+4 formats.

**In JavaScript:**

function isValidZipCode(zip) {
  const regex = /^\d{5}(-\d{4})?$/;
  return regex.test(zip);
}

**Real-world use case:** E-commerce checkout forms! I've used this pattern many times to validate shipping addresses.

**Note:** This just checks the format — it doesn't verify that the zip code actually exists. For that, you'd need an address verification API.

Pattern 7: Extract URLs from Text

This is great for finding all the links in a document, log file, or email. This pattern matches HTTP and HTTPS URLs.

**Pattern:** `https?://[^\s]+`

**Or, if you want to be more strict about valid URL characters:** `https?://[a-zA-Z0-9-._~:/?#[\]@!$&'()*+,;=]+`

**What it matches:**

- ✅ "http://example.com"

- ✅ "https://example.com/path/to/page"

- ✅ "https://example.com/path?query=123&param=test"

- ✅ "https://example.com/page#anchor"

- ✅ "http://sub.domain.example.co.uk/path"

**Breakdown:**

- `http` — Literal "http"

- `s?` — Optional "s" (so both HTTP and HTTPS work)

- `://` — Literal "://"

- `[^\s]+` — One or more characters that are NOT whitespace (this matches the rest of the URL)

**In JavaScript:**

function extractUrls(text) {
  const regex = /https?:\/\/[^\s]+/g;
  return text.match(regex) || [];
}

**Caveat:** This will sometimes include trailing punctuation if the URL is at the end of a sentence. For example, in "Check out https://example.com!", it will match "https://example.com!" (including the exclamation mark). You can clean this up afterwards if needed, or use a more complex pattern that excludes trailing punctuation.

**My approach:** I usually use this simple pattern to extract, then process the results to trim any trailing punctuation that shouldn't be there.

**Real-world use case:** I used this to build a link checker that scans documentation for broken links — it extracted all the URLs, then I fetched each one to check the status code.

Pattern 8: Parse Log Lines (Advanced Example)

This is a more advanced pattern that shows how to use groups to extract structured data from unstructured log lines. I use patterns like this all the time for debugging.

**Let's say we have log lines like this:**

2026-06-20 10:30:00 [INFO] User 123 logged in from 192.168.1.100
2026-06-20 10:30:05 [ERROR] Failed login for user 456 from 10.0.0.5
2026-06-20 10:30:10 [WARN] Rate limit hit for user 789

**Pattern to parse them:** `^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(\w+)\] (.*)$`

**Breakdown with groups:**

- Group 1: `(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})` — The timestamp

- `\[` — Literal [ (needs escaping!)

- Group 2: `(\w+)` — The log level (INFO, ERROR, WARN, etc.)

- `\]` — Literal ]

- Group 3: `(.*)` — The message itself

**In JavaScript:**

function parseLogLine(line) {
  const regex = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(\w+)\] (.*)$/;
  const match = line.match(regex);
  if (match) {
    return {
      timestamp: match[1],
      level: match[2],
      message: match[3]
    };
  }
  return null;
}

**You can get even more specific** if your log lines have a consistent format. For example, if login messages always have "User X logged in from Y", you could extract the user ID and IP with a more specific pattern.

**Real-world use case:** I once had to process 10GB of log files to find all error messages. I used a pattern like this to parse each line, filter for errors, and output a summary — it would have taken me days to do manually, but the regex did it in minutes.

**Pro tip:** For very large log files, use command-line tools like `grep`, `awk`, or `sed` — they're optimized for this kind of work. But the same pattern concepts apply!

How to Build Regex Patterns Incrementally

Don't try to write the perfect regex on the first try. I never do. I build them step by step, testing at each stage. Let's walk through my process with an example.

**Example: Let's build a pattern for US phone numbers**

We want to match numbers like: "123-456-7890", "(123) 456-7890", "123.456.7890", etc.

**Step 1: Start with the most basic version** — just 10 digits: `\d{10}`

This matches "1234567890" — a good start.

**Step 2: Add anchors** to match the whole string: `^\d{10}$`

Now it won't match strings with extra characters around the digits.

**Step 3: Add formatting separators** — let's start with dashes: `^\d{3}-\d{3}-\d{4}$`

Now it matches "123-456-7890".

**Step 4: Allow multiple separator types** — use a character class: `^\d{3}[-.]\d{3}[-.]\d{4}$`

Now it matches dashes or dots: "123-456-7890" or "123.456.7890".

**Step 5: Add optional parentheses** for the area code: `^\(?\d{3}\)?[-.]?\d{3}[-.]\d{4}$`

The `\(?` and `\)?` make the parentheses optional. The `[-.]?` makes the separator after the area code optional.

Now it matches: "(123)456-7890", "123-456-7890", "(123) 456-7890" — wait, no, we didn't allow spaces yet.

**Step 6: Allow optional spaces** — let's refine it: `^\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$`

Now `[-.\s]` allows dash, dot, or space. The `?` makes it optional.

**Step 7: Test with real examples** — try all the formats you want to match, and tweak as needed.

**The final pattern might look like:** `^\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$`

**My process in summary:**

1. Start simple — match the core case

2. Add anchors first if it's for validation

3. Add complexity incrementally

4. Test at each step

5. Test with both valid AND invalid inputs

6. If it gets too complicated, stop and ask: is regex the right tool here?

And remember: our Regex Tester is perfect for this incremental approach — you can test as you build.

When NOT to Use Regex

Regex is powerful, but it's not the right tool for everything. I've learned this the hard way — I've built regex monsters that I couldn't debug later. Here's when to consider other approaches.

**Don't use regex for HTML/XML parsing.** HTML is not a regular language — regex can't properly handle nested elements. For example, trying to parse HTML with regex will fail on things like nested divs or comments. Use an HTML parser library instead.

**Don't use regex for full email validation.** As we discussed, a perfect RFC-compliant email regex is enormous. Just check for the basics with a simple pattern, then send a confirmation email.

**Don't use regex for full date validation.** Regex can check the format, but not whether the date is actually valid (like February 30th). Use a date library instead.

**Don't use regex for complex nested structures.** JSON, CSV with quoted fields containing commas, programming languages — these are all hard or impossible to parse correctly with regex.

**Don't use regex when simple string methods are enough.** In JavaScript: `text.includes('hello')`, `text.startsWith('https://')`, `text.split(',')` — these are all simpler and more readable than regex for simple cases.

**Don't use regex when the pattern is too complicated.** If you can't read your own regex the next day, it's too complicated. Break it into smaller steps, use code instead, or add lots of comments.

**My rule of thumb:** If the regex is longer than about 30 characters and I can't understand it at a glance, I think twice about whether to use it. Sometimes code is more readable and maintainable.

That said — regex is still one of the most useful tools in my toolbox. Use it when it's the right tool, and don't overcomplicate it.

Tools

Try These Tools

Open the tools that go with this tutorial and practice what you learned.

Navigation

Keep Learning