Java Regular Expressions: Pattern, Matcher, and Common Patterns
Java's java.util.regex package provides Pattern (a compiled regex) and Matcher (an engine that applies the regex to a string). Compiling a Pattern once and reusing the Matcher is the performant approach. Java supports standard regex syntax including groups, named groups, lookaheads, and quantifiers, and String methods like matches(), replaceAll(), and split() are convenience wrappers around the same engine.
Contents
Pattern and Matcher Basics
Pattern.compile()Patternstatic finalpattern.matcher(input)Matcherfind()matches()lookingAt()
import java.util.regex.*;
// String.matches() — checks if the ENTIRE string matches (convenience, not reusable)
boolean ok = "hello123".matches("[a-z]+\\d+"); // true
// Double backslash \\ in Java string = single \ in regex
// Pattern.compile() — compile once, use many times
Pattern digits = Pattern.compile("\\d+");
Pattern words = Pattern.compile("\\b[A-Za-z]+\\b");
// Matcher.find() — finds next match anywhere in the string
String text = "Order 123 shipped, invoice 456 pending";
Matcher m = digits.matcher(text);
while (m.find()) {
System.out.println("Found: " + m.group() +
" at [" + m.start() + "," + m.end() + ")");
}
// Found: 123 at [6,9)
// Found: 456 at [27,30)
// Matcher.matches() — the ENTIRE string must match
Matcher full = Pattern.compile("\\d{3}").matcher("123");
System.out.println(full.matches()); // true
Matcher notFull = Pattern.compile("\\d{3}").matcher("123abc");
System.out.println(notFull.matches()); // false
// Matcher.lookingAt() — matches from beginning (not necessarily the whole string)
Matcher beginning = Pattern.compile("\\d+").matcher("123abc");
System.out.println(beginning.lookingAt()); // true
// Reset a matcher to reuse with a new input
m.reset("Invoice 789 sent");
while (m.find()) System.out.println(m.group()); // 789
Capturing Groups and Named Groups
Parentheses in a regex create a capturing group; matcher.group(n)(?<name>pattern)matcher.group("name")(?:pattern)
// Numbered groups — (pattern) creates group 1, 2, ...
String date = "2025-03-15";
Pattern datePattern = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher dm = datePattern.matcher(date);
if (dm.matches()) {
String year = dm.group(1); // "2025"
String month = dm.group(2); // "03"
String day = dm.group(3); // "15"
System.out.printf("Year: %s, Month: %s, Day: %s%n", year, month, day);
}
// Named groups — (?<name>pattern) — Java 7+
Pattern named = Pattern.compile(
"(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})");
Matcher nm = named.matcher(date);
if (nm.matches()) {
System.out.println(nm.group("year")); // "2025"
System.out.println(nm.group("month")); // "03"
System.out.println(nm.group("day")); // "15"
}
// Collecting all matches with groups
String input = "Alice:90, Bob:85, Charlie:92";
Pattern kvp = Pattern.compile("(?<name>[A-Za-z]+):(?<score>\\d+)");
Matcher kvm = kvp.matcher(input);
while (kvm.find()) {
System.out.printf("%s scored %s%n", kvm.group("name"), kvm.group("score"));
}
// Alice scored 90
// Bob scored 85
// Charlie scored 92
// Non-capturing group — (?:pattern) — groups without capturing
Pattern nc = Pattern.compile("(?:https?|ftp)://([\\w./-]+)");
Matcher ncm = nc.matcher("https://example.com/page");
if (ncm.find()) {
System.out.println(ncm.group(1)); // "example.com/page" (group 1, not https)
}
replace and split
String.replaceAll(regex, replacement)replaceFirst()$1$2${name}Matcher.replaceAll(Function)split()
// String.replaceAll — replace all matches (regex)
String cleaned = "hello world java".replaceAll("\\s+", " ");
System.out.println(cleaned); // "hello world java"
// String.replaceFirst — replace first match only
String first = "aaa bbb aaa".replaceFirst("aaa", "XXX");
System.out.println(first); // "XXX bbb aaa"
// Back-references in replacement ($1 for group 1)
String swapped = "2025-03-15".replaceAll("(\\d{4})-(\\d{2})-(\\d{2})", "$3/$2/$1");
System.out.println(swapped); // "15/03/2025"
// Named group back-reference (${name})
String reordered = "2025-03-15".replaceAll(
"(?<y>\\d{4})-(?<m>\\d{2})-(?<d>\\d{2})",
"${d}/${m}/${y}");
System.out.println(reordered); // "15/03/2025"
// Matcher.replaceAll with Function (Java 9+)
String result = Pattern.compile("\\d+")
.matcher("order 5 of 10 items")
.replaceAll(mr -> String.valueOf(Integer.parseInt(mr.group()) * 2));
System.out.println(result); // "order 10 of 20 items"
// String.split with regex
String csv = "a,b,,c,d";
String[] parts = csv.split(","); // ["a","b","","c","d"]
String[] limit = csv.split(",", 3); // ["a","b",",,c,d"] — max 3 parts
String[] noEmpty = csv.split(",+"); // ["a","b","c","d"] — skip empty
Flags and Modifiers
Flags change how the regex engine interprets the pattern. CASE_INSENSITIVEMULTILINE^$DOTALL.|Pattern.compile()(?i)(?m)(?s)
// CASE_INSENSITIVE
Pattern p = Pattern.compile("hello", Pattern.CASE_INSENSITIVE);
System.out.println(p.matcher("HELLO WORLD").find()); // true
// Inline flag (?i) — applies within the pattern
System.out.println("HELLO".matches("(?i)hello")); // true
// MULTILINE — ^ and $ match start/end of each line, not just the whole input
String multi = "first\nsecond\nthird";
Pattern ml = Pattern.compile("^\\w+$", Pattern.MULTILINE);
Matcher mm = ml.matcher(multi);
while (mm.find()) System.out.println(mm.group()); // first, second, third
// DOTALL — . matches newlines too (default: . does NOT match \n)
Pattern dot = Pattern.compile("begin.*end", Pattern.DOTALL);
System.out.println(dot.matcher("begin\nmiddle\nend").find()); // true
// Combining flags with |
Pattern combined = Pattern.compile("pattern",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
// LITERAL — treat the pattern as a literal string (no special chars)
Pattern literal = Pattern.compile("a.b", Pattern.LITERAL);
System.out.println(literal.matcher("a.b").matches()); // true
System.out.println(literal.matcher("axb").matches()); // false (dot is literal)
// Pattern.quote — escape a string for use as a literal in a regex
String userInput = "1+1=2";
String safe = Pattern.quote(userInput); // "\\Q1+1=2\\E"
"formula: 1+1=2".replaceAll(safe, "ANSWER"); // safe replacement
Common Patterns
These patterns cover the most common real-world validation and extraction tasks. Declare them as static finalPattern.compile()
// Email (simplified)
Pattern EMAIL = Pattern.compile("^[\\w.+-]+@[\\w-]+\\.[a-zA-Z]{2,}$");
System.out.println(EMAIL.matcher("user@example.com").matches()); // true
System.out.println(EMAIL.matcher("invalid@").matches()); // false
// US phone number variants
Pattern PHONE = Pattern.compile("\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}");
System.out.println(PHONE.matcher("(555) 123-4567").find()); // true
System.out.println(PHONE.matcher("555.123.4567").find()); // true
// IPv4 address (0-255 per octet)
Pattern IPV4 = Pattern.compile(
"\\b(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}" +
"(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\b");
// URL extraction
Pattern URL = Pattern.compile("https?://[\\w./?=&%-]+");
Matcher um = URL.matcher("Visit https://cscode.io and https://example.com");
while (um.find()) System.out.println(um.group());
// Alphanumeric identifier
Pattern ID = Pattern.compile("^[A-Za-z][A-Za-z0-9_]{2,29}$");
// Numbers (int or float)
Pattern NUMBER = Pattern.compile("-?\\d+(\\.\\d+)?");
// Whitespace normalization
String normalized = " Hello World ".trim().replaceAll("\\s+", " ");
System.out.println(normalized); // "Hello World"
Patternstatic finalPattern.compile()Matcher