Validating Input with Regex
Every sign-up form checks that an email looks like an email, a phone number has the right digits, and a password is strong enough. Regular expressions are the natural tool for these format checks. This lesson builds four real validators step by step, explains each piece, and ends with a warning: a carelessly written pattern can take seconds on certain inputs, which attackers can use to slow a server down.
Four Validators
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Validators {
static final Pattern EMAIL =
Pattern.compile("[\\w.+-]+@[\\w-]+(\\.[\\w-]+)+");
static final Pattern PHONE =
Pattern.compile("(\\+91[ -]?)?[6-9]\\d{9}");
static final Pattern STRONG = Pattern.compile(
"(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)(?=.*[^A-Za-z0-9]).{8,}");
static final Pattern LOG = Pattern.compile(
"(?<date>\\d{4}-\\d{2}-\\d{2}) (?<time>\\d{2}:\\d{2}:\\d{2}) "
+ "(?<level>INFO|WARN|ERROR) (?<msg>.*)");
public static void main(String[] args) {
for (String e : new String[] {"navika@example.com",
"srihitha.a@mail.co.in", "advitha@", "no spaces@x.com"}) {
System.out.println("email " + e + " -> "
+ EMAIL.matcher(e).matches());
}
for (String p : new String[] {"9876543210", "+91 9876543210",
"1234567890", "98765"}) {
System.out.println("phone " + p + " -> "
+ PHONE.matcher(p).matches());
}
for (String p : new String[] {"Navika@2026", "navika2026", "Short1!"}) {
System.out.println("password " + p + " -> "
+ STRONG.matcher(p).matches());
}
Matcher m = LOG.matcher(
"2026-11-12 09:15:02 ERROR Payment failed for Advitha");
if (m.matches()) {
System.out.println(m.group("level") + " at " + m.group("time")
+ ": " + m.group("msg"));
}
}
}email navika@example.com -> true
email srihitha.a@mail.co.in -> true
email advitha@ -> false
email no spaces@x.com -> false
phone 9876543210 -> true
phone +91 9876543210 -> true
phone 1234567890 -> false
phone 98765 -> false
password Navika@2026 -> true
password navika2026 -> false
password Short1! -> false
ERROR at 09:15:02: Payment failed for AdvithaHow Each Pattern Works
Email: [\w.+-]+ is the part before @ (letters, digits, _, ., +, -), then @, then a domain name [\w-]+, then one or more .something parts, (\.[\w-]+)+. That accepts mail.co.in and rejects a missing domain or a space.
Indian mobile number: an optional +91 with an optional space or dash, then a first digit from 6 to 9, then exactly 9 more digits.
Strong password: each lookahead (?=...) checks one rule without consuming any characters, so all four checks start from the beginning: a capital, a small letter, a digit, a symbol. Then .{8,} requires 8 or more characters in total. "Short1!" fails only because it's 7 characters long.
Log line: named groups pull out the date, time, level and message, so the code reads m.group("level") instead of counting brackets.
Know the Limits
A regex checks format, not truth. navika@example.com can match the pattern and still not exist. Real email rules are so complex that no practical regex covers them all, so production systems check a reasonable format and then send a confirmation email. For dates, parse with LocalDate.parse (which knows that February has no 30th) instead of a regex.
When a Pattern Becomes Slow
The regex engine backtracks: when part of a pattern fails, it goes back and tries other ways to split the text. Some patterns give it an enormous number of ways to try. Here's a pattern meant to find "the 12th comma-separated field starting with P", and a safer version of it:
import java.util.regex.Pattern;
public class Backtracking {
public static void main(String[] args) {
Pattern risky = Pattern.compile("(.*?,){11}P");
Pattern safe = Pattern.compile("([^,]*,){11}P");
for (int n : new int[] {15, 20, 25}) {
StringBuilder line = new StringBuilder();
for (int i = 1; i <= n; i++) {
line.append(i).append(',');
}
long t0 = System.nanoTime();
risky.matcher(line).find();
long t1 = System.nanoTime();
safe.matcher(line).find();
long t2 = System.nanoTime();
System.out.println(n + " fields: risky " + (t1 - t0) / 1_000_000
+ " ms, safe " + (t2 - t1) / 1_000_000 + " ms");
}
}
}15 fields: risky 8 ms, safe 0 ms
20 fields: risky 62 ms, safe 0 ms
25 fields: risky 831 ms, safe 0 msNeither line contains a P, so both must fail. The risky pattern's .*? can also swallow commas, so the engine tries a huge number of ways to divide the commas among the 11 groups, and the time explodes as the line grows. The safe pattern's [^,]* can't cross a comma, so there's only one way to try, and it fails instantly.
When a pattern like this checks user input on a web server, an attacker can send a crafted string that ties up the CPU. This is called ReDoS (Regular expression Denial of Service).
Common Mistakes
Using find() for validation. "abc 9876543210 xyz" would "pass" a phone check; use matches().
Trying to validate everything with one giant regex. Split rules, as the password lookaheads do, or check some rules in plain code.
Copying a huge pattern from the internet without testing it. Test both valid and invalid inputs, including long ones.
Interview Corner
How would you validate a password with regex?
Use lookaheads, one per rule, followed by a length check: (?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}. Each lookahead asserts that the rule is met somewhere without consuming characters, so all rules are checked from the start, and .{8,} enforces the minimum length. Use matches() so the whole input is checked.
What is catastrophic backtracking?
It's when a regex engine has exponentially many ways to try matching a failing input, usually because of nested or overlapping quantifiers such as (a+)+ or (.*?,){n}. Matching time can explode from milliseconds to seconds or more, which attackers can exploit as ReDoS. The fix is to make each part of the pattern match only what it should, for example [^,]* instead of .*?, and to limit input length.
Practice
- Easy: Write a validator for 6-digit Indian PIN codes that can't start with 0, and test it on
"411001","011001"and"41100". - Harder: Extend the log parser to also accept a
DEBUGlevel and count how many lines of each level appear in a small multi-line string.
Key Takeaways
Was this tutorial helpful?