Java Regular Expressions (RegEx)
A complete guide to Java Regular Expressions β regex syntax (character classes, quantifiers, anchors, groups, lookahead, lookbehind), Pattern and Matcher API, all Matcher methods, regex flags, String regex methods, real-world validation patterns for email, phone, password, and URL, plus best practices with examples.
Last Updated
March 2026
Read Time
20 min
Level
Beginner to Intermediate
What is Regular Expression in Java?
A Regular Expression (regex or regexp) is a precisely defined sequence of characters that describes a search pattern. In Java, regular expressions are used to match, search, extract, validate, and replace text within strings β from simple substring checks to complex pattern matching across thousands of lines of input.
Java provides regex support through the java.util.regex package, which contains two core classes: Pattern (the compiled regex) and Matcher (the engine that applies the pattern to an input string). The String class also exposes four convenience methods β matches(), replaceAll(), replaceFirst(), and split() β that accept regex patterns directly.
Regular expressions are indispensable in real-world Java applications: validating user input (email, phone, password), parsing log files, sanitizing form data, extracting structured data from unstructured text, routing URLs in web frameworks, and tokenizing DSL syntax. Every Java developer at the professional level must be fluent in regex.
import java.util.regex.*;
public class RegexIntro {
public static void main(String[] args) {
String text = "Order #1042 placed on 2026-03-16 for βΉ4500";
// Find all numbers in the text
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(text);
System.out.println("Numbers found in text:");
while (matcher.find()) {
System.out.println(" '" + matcher.group() + "' at index " + matcher.start());
}
}
}Output
Numbers found in text: '1042' at index 7 '2026' at index 22 '03' at index 27 '16' at index 30 '4500' at index 37Regex Syntax β Complete Quick Reference
Every regex is built from a small set of syntax elements. The table below is your complete reference for all Java regex syntax β bookmark it.
Character Classes β Matching Sets of Characters
Character classes define a set of characters that a single position in the input can match. They are enclosed in square brackets [] and give you fine-grained control over which characters are acceptable at a given position.
import java.util.regex.*;
public class CharacterClassDemo {
public static void main(String[] args) {
// Simple class β any one of these characters
print("[aeiou]", "elephant"); // e, e, a
// Negated class β anything NOT in this set
print("[^aeiou\\s]", "Hello World"); // H,l,l,W,r,l,d
// Range β a through z
print("[a-z]+", "Hello123World"); // ello, orld
// Union of classes β letter OR digit
print("[a-zA-Z0-9]+", "Hi! I am R2D2."); // Hi, I, am, R2D2
// Intersection β letter AND NOT vowel (consonants only)
// Uses && inside [] for intersection
print("[a-z&&[^aeiou]]+", "programming"); // prgrmmng
// Predefined shorthand classes
print("\\d+", "Room 404 on Floor 2"); // 404, 2
print("\\w+", "Hello, World! 2026"); // Hello, World, 2026
print("\\s+", "one two\tthree"); // ' ', '\t'
}
static void print(String regex, String input) {
Matcher m = Pattern.compile(regex).matcher(input);
System.out.print("Regex [" + regex + "] β ");
while (m.find()) System.out.print("'" + m.group() + "' ");
System.out.println();
}
}Output
Regex [[aeiou]] β 'e' 'e' 'a' Regex [[^aeiou\s]] β 'H' 'l' 'l' 'W' 'r' 'l' 'd' Regex [[a-z]+] β 'ello' 'orld' Regex [[a-zA-Z0-9]+] β 'Hi' 'I' 'am' 'R2D2' Regex [[a-z&&[^aeiou]]+] β 'prgrmmng' Regex [\d+] β '404' '2' Regex [\w+] β 'Hello' 'World' '2026' Regex [\s+] β ' ' '\t'Quantifiers β Controlling Repetition
Quantifiers specify how many times the preceding element must appear for a match. Java supports three flavors of every quantifier: greedy (default β matches as much as possible), reluctant/lazy (matches as little as possible, add ?), and possessive (matches as much as possible, never backtracks, add +).
import java.util.regex.*;
public class QuantifierDemo {
public static void main(String[] args) {
String html = "<b>bold</b> and <i>italic</i>";
// GREEDY β matches from FIRST < to LAST > (too much)
Matcher greedy = Pattern.compile("<.+>").matcher(html);
if (greedy.find()) {
System.out.println("Greedy: '" + greedy.group() + "'");
}
// Output: '<b>bold</b> and <i>italic</i>'
// LAZY β matches from FIRST < to NEAREST > (correct for HTML tags)
Matcher lazy = Pattern.compile("<.+?>").matcher(html);
System.out.print("Lazy: ");
while (lazy.find()) {
System.out.print("'" + lazy.group() + "' ");
}
System.out.println();
// Output: '<b>' '</b>' '<i>' '</i>'
// Exact quantifiers
String pins = "PIN: 1234, invalid: 56789, ok: 9876";
Matcher fourDigit = Pattern.compile("\\b\\d{4}\\b").matcher(pins);
System.out.print("4-digit PINs: ");
while (fourDigit.find()) {
System.out.print("'" + fourDigit.group() + "' ");
}
// Output: '1234' '9876' (56789 has 5 digits β not matched)
}
}Output
Greedy: '<b>bold</b> and <i>italic</i>' Lazy: '<b>' '</b>' '<i>' '</i>' 4-digit PINs: '1234' '9876'Anchors and Word Boundaries
Anchors match a position in the string β not a character. They are zero-width assertions: they consume no characters but constrain where in the string a match can occur.
import java.util.regex.*;
public class AnchorDemo {
public static void main(String[] args) {
// ^ β match only at start of string
System.out.println("ERROR: fail".matches("^ERROR.*")); // true
System.out.println("INFO: ERROR".matches("^ERROR.*")); // false
// $ β match only at end of string
System.out.println("image.png".matches(".*\\.png$")); // true
System.out.println("image.png.bak".matches(".*\\.png$")); // false
// \b β word boundary: matches where a word starts or ends
String text = "The cat scattered the catalog";
Matcher wb = Pattern.compile("\\bcat\\b").matcher(text);
System.out.print("\\bcat\\b matches: ");
while (wb.find()) System.out.print("'" + wb.group() + "'@" + wb.start() + " ");
// Only 'cat' at position 4 β not 'cat' inside 'scattered' or 'catalog'
System.out.println();
// \A and \Z β absolute start/end (unaffected by MULTILINE flag)
String multiLine = "first line\nsecond line\nthird line";
Matcher absStart = Pattern.compile("\\Afirst").matcher(multiLine);
System.out.println("\\Afirst found: " + absStart.find()); // true
// MULTILINE flag changes ^ and $ to match per line
Matcher perLine = Pattern.compile("^second",
Pattern.MULTILINE).matcher(multiLine);
System.out.println("^second with MULTILINE: " + perLine.find()); // true
}
}Output
true false true false \bcat\b matches: 'cat'@4 \Afirst found: true ^second with MULTILINE: trueGroups and Backreferences
Groups serve two purposes in regex: they can group tokens together (apply a quantifier to multiple characters) and they can capture the matched text for later retrieval or backreference. Java supports numbered groups, named groups, and non-capturing groups.
import java.util.regex.*;
public class GroupDemo {
public static void main(String[] args) {
// ββ NUMBERED GROUPS βββββββββββββββββββββββββββββββββββββββββββββ
// Pattern: (yyyy)-(MM)-(dd) β three capturing groups
String dateStr = "Invoice date: 2026-03-16, Due: 2026-04-15";
Pattern datePattern = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher m = datePattern.matcher(dateStr);
while (m.find()) {
System.out.println("Full match : " + m.group(0)); // entire match
System.out.println("Year : " + m.group(1)); // group 1
System.out.println("Month : " + m.group(2)); // group 2
System.out.println("Day : " + m.group(3)); // group 3
System.out.println("---");
}
// ββ NAMED GROUPS ββββββββββββββββββββββββββββββββββββββββββββββββ
Pattern named = Pattern.compile(
"(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})");
Matcher mn = named.matcher("2026-03-16");
if (mn.matches()) {
System.out.println("Named year : " + mn.group("year"));
System.out.println("Named month: " + mn.group("month"));
}
// ββ NON-CAPTURING GROUP (?:) βββββββββββββββββββββββββββββββββββββ
// Groups without capturing β saves memory, avoids polluting group numbers
Pattern proto = Pattern.compile("(?:https?|ftp)://([\\w./-]+)");
Matcher mp = proto.matcher("Visit https://techsustainify.com/java");
if (mp.find()) {
System.out.println("Host+path : " + mp.group(1)); // group 1 = after ://
}
// ββ BACKREFERENCE \1 βββββββββββββββββββββββββββββββββββββββββββββ
// Match a repeated word (e.g., typo detection)
Pattern dupe = Pattern.compile("\\b(\\w+)\\s+\\1\\b");
Matcher md = dupe.matcher("The the quick brown fox fox jumps");
System.out.print("Duplicate words: ");
while (md.find()) {
System.out.print("'" + md.group(1) + "' ");
}
System.out.println();
}
}Output
Full match : 2026-03-16 Year : 2026 Month : 03 Day : 16 --- Full match : 2026-04-15 Year : 2026 Month : 04 Day : 15 --- Named year : 2026 Named month: 03 Host+path : techsustainify.com/java Duplicate words: 'The' 'fox'Lookahead and Lookbehind β Zero-Width Assertions
Lookahead and Lookbehind are zero-width assertions β they check the context around a position without consuming characters. The matched text of the lookahead/lookbehind itself is not included in the match result. This makes them ideal for asserting surrounding context without capturing it.
import java.util.regex.*;
public class LookaroundDemo {
public static void main(String[] args) {
// ββ POSITIVE LOOKAHEAD (?=...) ββββββββββββββββββββββββββββββββββ
// Extract numbers that are followed by 'px' (CSS values)
String css = "margin: 16px; padding: 8px; border: 1em; font: 14px;";
Matcher la = Pattern.compile("\\d+(?=px)").matcher(css);
System.out.print("px values: ");
while (la.find()) System.out.print(la.group() + " ");
// Output: 16 8 14 (1em is excluded)
System.out.println();
// ββ NEGATIVE LOOKAHEAD (?!...) ββββββββββββββββββββββββββββββββββ
// Find numbers NOT followed by 'px'
Matcher nla = Pattern.compile("\\d+(?!px)\\b").matcher(css);
System.out.print("non-px values: ");
while (nla.find()) System.out.print(nla.group() + " ");
// Output: 1 (from '1em')
System.out.println();
// ββ POSITIVE LOOKBEHIND (?<=...) ββββββββββββββββββββββββββββββββ
// Extract amounts after currency symbol
String receipt = "Total: βΉ1500, Discount: βΉ200, Payable: βΉ1300";
Matcher lb = Pattern.compile("(?<=βΉ)\\d+").matcher(receipt);
System.out.print("Amounts: ");
while (lb.find()) System.out.print(lb.group() + " ");
// Output: 1500 200 1300
System.out.println();
// ββ NEGATIVE LOOKBEHIND (?<!...) ββββββββββββββββββββββββββββββββ
// Match numbers NOT preceded by '#' (not hex codes)
String colors = "color: #FF5733; opacity: 0.8; z-index: 10";
Matcher nlb = Pattern.compile("(?<!#)\\b\\d+\\.?\\d*\\b").matcher(colors);
System.out.print("Non-hex numbers: ");
while (nlb.find()) System.out.print(nlb.group() + " ");
// Output: 0.8 10
}
}Output
px values: 16 8 14 non-px values: 1 Amounts: 1500 200 1300 Non-hex numbers: 0.8 10Pattern and Matcher Classes β The Core API
The java.util.regex.Pattern class represents a compiled regular expression. Compilation parses the regex string into an internal finite automaton β this is expensive. java.util.regex.Matcher is the engine that applies a compiled Pattern to a specific input string.
import java.util.regex.*;
import java.util.*;
public class PatternMatcherAPI {
public static void main(String[] args) {
// ββ CREATING A PATTERN βββββββββββββββββββββββββββββββββββββββββββ
// compile() β parse once, reuse many times
Pattern emailPattern = Pattern.compile(
"[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}");
// ββ CREATING A MATCHER βββββββββββββββββββββββββββββββββββββββββββ
String input = "Contact: rahul@techsustainify.com or support@example.org";
Matcher m = emailPattern.matcher(input);
// ββ find() β locate next match βββββββββββββββββββββββββββββββββββ
List<String> emails = new ArrayList<>();
while (m.find()) {
emails.add(m.group());
}
System.out.println("Emails: " + emails);
// ββ Reset matcher for a new input (avoid recompiling Pattern) βββ
String input2 = "admin@company.in";
m.reset(input2);
System.out.println("Valid email: " + m.matches());
// ββ matches() β entire string must match βββββββββββββββββββββββββ
System.out.println("not-valid@".matches(
"[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}")); // false
// ββ start() end() group() β position and text ββββββββββββββββββββ
Matcher pos = Pattern.compile("\\d{4}").matcher("Year 2026 and 2027");
while (pos.find()) {
System.out.printf("Found '%s' at [%d, %d)%n",
pos.group(), pos.start(), pos.end());
}
// ββ Pattern.split() β split by regex βββββββββββββββββββββββββββββ
String csv = "Java,,Python, ,Go,Rust";
String[] parts = Pattern.compile(",\\s*,?").split(csv);
System.out.println(Arrays.toString(parts));
// ββ Pattern.quote() β escape a literal string for regex ββββββββββ
String literal = "3.14"; // dot means 'any char' in regex
String safe = Pattern.quote(literal); // \Q3.14\E
System.out.println("3X14".matches(safe)); // false β literal dot only
System.out.println("3.14".matches(safe)); // true
}
}Output
Emails: [rahul@techsustainify.com, support@example.org] Valid email: true false Found '2026' at [5, 9) Found '2027' at [14, 18) [Java, Python, Go, Rust] false trueMatcher Methods β Complete Reference
The Matcher class is the workhorse of Java regex. Every useful operation β finding, extracting, replacing, and positioning β goes through these methods.
Regex Flags β Modifying Match Behavior
Regex flags modify how the matching engine interprets both the pattern and the input. They can be passed to Pattern.compile(regex, flags) or embedded directly in the pattern using inline flag syntax (?flag).
import java.util.regex.*;
public class RegexFlagsDemo {
public static void main(String[] args) {
// CASE_INSENSITIVE β flag constant
Pattern ci = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
System.out.println(ci.matcher("I love JAVA!").find()); // true
// CASE_INSENSITIVE β inline (?i)
System.out.println("HELLO".matches("(?i)hello")); // true
// MULTILINE β ^ matches start of each line
String log = "INFO: Server started\nERROR: DB failed\nINFO: Retry";
Matcher ml = Pattern.compile("^ERROR.*", Pattern.MULTILINE).matcher(log);
System.out.println("ERROR lines:");
while (ml.find()) System.out.println(" " + ml.group());
// DOTALL β dot matches newline too
String multiLine = "START\nMIDDLE\nEND";
System.out.println(multiLine.matches("START.*END")); // false β dot skips \n
System.out.println(multiLine.matches("(?s)START.*END")); // true β (?s) enables DOTALL
// COMMENTS β write readable regex with whitespace and comments
Pattern readable = Pattern.compile(
"([a-zA-Z0-9._%+\\-]+) # local part\n" +
"@ # at sign\n" +
"([a-zA-Z0-9.\\-]+) # domain\n" +
"\\.([a-zA-Z]{2,}) # TLD",
Pattern.COMMENTS);
System.out.println(readable.matcher("dev@techsustainify.com").matches()); // true
// Multiple flags combined with bitwise OR
Pattern multi = Pattern.compile("^error",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
System.out.println(multi.matcher("INFO\nERROR: crash").find()); // true
}
}Output
true true ERROR lines: ERROR: DB failed false true true trueString Class Regex Methods
The String class provides four built-in regex convenience methods that internally use Pattern and Matcher. They are ideal for one-off regex operations, but compile the pattern on every call β so use Pattern.compile() directly for performance-critical or frequently-called code.
public class StringRegexMethods {
public static void main(String[] args) {
// ββ matches(regex) βββββββββββββββββββββββββββββββββββββββββββββββ
// Equivalent to Pattern.compile(regex).matcher(str).matches()
// ENTIRE string must match the pattern
String phone = "9876543210";
System.out.println(phone.matches("[6-9]\\d{9}")); // true β valid Indian mobile
System.out.println("123456".matches("[6-9]\\d{9}")); // false
// ββ replaceAll(regex, replacement) βββββββββββββββββββββββββββββββ
// Replaces ALL occurrences matching the pattern
String dirty = " Hello World ";
System.out.println(dirty.replaceAll("\\s+", " ").trim()); // 'Hello World'
// Replace non-alphanumeric chars with underscore
String filename = "my file (2026).txt";
System.out.println(filename.replaceAll("[^a-zA-Z0-9.]", "_")); // my_file__2026_.txt
// ββ replaceFirst(regex, replacement) βββββββββββββββββββββββββββββ
// Replaces only the FIRST occurrence
String text = "Error on line 42. Error on line 87.";
System.out.println(text.replaceFirst("Error", "WARNING"));
// 'WARNING on line 42. Error on line 87.'
// ββ split(regex) βββββββββββββββββββββββββββββββββββββββββββββββββ
// Split by any whitespace sequence
String sentence = "Java is awesome";
String[] words = sentence.split("\\s+");
System.out.println(java.util.Arrays.toString(words)); // [Java, is, awesome]
// Split by comma and optional surrounding spaces
String csv = "Alice, Bob , Carol,Dave";
String[] names = csv.split("\\s*,\\s*");
System.out.println(java.util.Arrays.toString(names)); // [Alice, Bob, Carol, Dave]
// Split with limit β keep at most N tokens
String path = "com.techsustainify.java.regex";
String[] parts = path.split("\\.", 2); // limit=2
System.out.println(java.util.Arrays.toString(parts)); // [com, techsustainify.java.regex]
}
}Output
true false Hello World my_file__2026_.txt WARNING on line 42. Error on line 87. [Java, is, awesome] [Alice, Bob, Carol, Dave] [com, techsustainify.java.regex]Regex Matching Flow β Flowchart
The flowchart below shows the complete execution path from writing a regex pattern to obtaining a match result through the Pattern/Matcher API.
Code Execution Flow β from source to output
Real-World Validation Patterns
Here are battle-tested, production-ready regex patterns for the most common Java validation scenarios. Each pattern is explained token by token.
import java.util.regex.*;
public class Validators {
// β
Compile ONCE as static final β never recompile in methods
private static final Pattern MOBILE =
Pattern.compile("^[6-9]\\d{9}$");
private static final Pattern EMAIL =
Pattern.compile("^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$");
private static final Pattern PASSWORD =
Pattern.compile(
"^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$");
private static final Pattern PAN =
Pattern.compile("^[A-Z]{5}[0-9]{4}[A-Z]$");
public static boolean isValidMobile(String s) { return MOBILE.matcher(s).matches(); }
public static boolean isValidEmail(String s) { return EMAIL.matcher(s).matches(); }
public static boolean isStrongPassword(String s){ return PASSWORD.matcher(s).matches(); }
public static boolean isValidPAN(String s) { return PAN.matcher(s).matches(); }
public static void main(String[] args) {
System.out.println(isValidMobile("9876543210")); // true
System.out.println(isValidMobile("1234567890")); // false
System.out.println(isValidEmail("dev@tech.com")); // true
System.out.println(isValidEmail("bad@")); // false
System.out.println(isStrongPassword("Secure@123")); // true
System.out.println(isStrongPassword("weak")); // false
System.out.println(isValidPAN("ABCDE1234F")); // true
System.out.println(isValidPAN("abcde1234f")); // false
}
}Output
true false true false true false true falseBest Practices for Java Regular Expressions
Regex is powerful but can be slow, unmaintainable, or incorrect when misused. Following these best practices will save you from the most common regex pitfalls.
- βΆ
β 1. Compile Pattern Once as static final β Pattern.compile() is expensive. Calling it inside a frequently-executed method or loop wastes CPU. Declare Pattern as a private static final field so it is compiled exactly once when the class loads. This is the single most impactful regex performance optimization in Java.
- βΆ
β 2. Use Pattern.quote() for Literal Strings β When you need to match a literal string that may contain regex metacharacters (., *, +, ?, etc.), always wrap it with Pattern.quote(literal). This generates a \Q...\E escaped pattern that treats every character literally β preventing subtle bugs where a dot matches any character instead of a literal period.
- βΆ
β 3. Prefer Non-Capturing Groups (?:) Over Capturing Groups β If you only need a group for quantifier application (not for extracting text), use (?:...) instead of (...). Non-capturing groups consume no memory for group capture and keep group numbering clean, making patterns with many groups easier to maintain.
- βΆ
β 4. Use Lazy Quantifiers for HTML/XML Parsing β When parsing tag-delimited content, always use lazy quantifiers (.+? instead of .+). Greedy matching will consume from the first opening delimiter to the LAST closing delimiter in the string β almost never what you want. Use +? or *? to match the shortest possible token.
- βΆ
β 5. Anchor Validation Patterns with ^ and $ β For input validation (email, phone, password), always anchor your pattern with ^ at the start and $ at the end. Without anchors, a pattern like \d+ would match any string containing a digit β including 'hello123world' β rather than rejecting it. Use \A and \Z for absolute anchors unaffected by the MULTILINE flag.
- βΆ
β 6. Name Groups When Using More Than 2 β With more than two capturing groups, use named groups (?<name>...) and access them via group("name"). This makes complex patterns self-documenting and protects code from breaking when groups are added or reordered.
- βΆ
β 7. Use COMMENTS Flag for Complex Patterns β For any pattern longer than 40 characters, use Pattern.COMMENTS to split it across lines with inline # comments. A well-commented 8-line regex is infinitely easier to maintain than a single-line monster string.
- βΆ
β 8. Never Use Regex for Full HTML/XML Parsing β Regex cannot correctly parse nested or malformed HTML/XML. Use a proper DOM parser (jsoup, javax.xml) instead. Regex is appropriate only for extracting specific, well-defined patterns from HTML β not for parsing its structure.
- βΆ
β 9. Beware of Catastrophic Backtracking β Patterns like (a+)+ or (.*)(.*) on long strings can cause exponential backtracking, freezing the JVM for minutes or crashing with a stack overflow. Avoid nested quantifiers on the same character class. Use possessive quantifiers (++) or atomic groups (?>...) where backtracking is unnecessary.
Real-World Code Examples
Example 1 β Log File Analyzer with Regex
import java.util.regex.*;
import java.util.*;
public class LogAnalyzer {
// Apache Combined Log Format pattern
// 127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /index.html HTTP/1.1" 200 2326
private static final Pattern LOG_PATTERN = Pattern.compile(
"(?<ip>\\d{1,3}(?:\\.\\d{1,3}){3})" + // IP address
" \\S+ \\S+ " + // ident and user
"\\[(?<datetime>[^\\]]+)\\] " + // datetime
"\"(?<method>\\w+) (?<path>\\S+)[^\"]*\"" + // request line
" (?<status>\\d{3})" + // HTTP status
" (?<bytes>\\d+|-)"); // bytes sent
public static void main(String[] args) {
String[] logs = {
"192.168.1.1 - alice [16/Mar/2026:10:00:01 +0530] \"GET /home HTTP/1.1\" 200 1523",
"10.0.0.2 - bob [16/Mar/2026:10:00:45 +0530] \"POST /api/login HTTP/1.1\" 401 98",
"192.168.1.5 - carol [16/Mar/2026:10:01:02 +0530] \"GET /dashboard HTTP/1.1\" 200 4096",
"172.16.0.9 - dave [16/Mar/2026:10:01:30 +0530] \"GET /missing HTTP/1.1\" 404 512"
};
Map<String, Integer> statusCount = new LinkedHashMap<>();
int totalBytes = 0;
for (String log : logs) {
Matcher m = LOG_PATTERN.matcher(log);
if (m.find()) {
String ip = m.group("ip");
String method = m.group("method");
String path = m.group("path");
String status = m.group("status");
String bytes = m.group("bytes");
statusCount.merge(status, 1, Integer::sum);
if (!bytes.equals("-")) totalBytes += Integer.parseInt(bytes);
System.out.printf("%-15s %-6s %-20s %s%n", ip, method, path, status);
}
}
System.out.println("\nStatus summary: " + statusCount);
System.out.println("Total bytes served: " + totalBytes);
}
}Output
192.168.1.1 GET /home 200 10.0.0.2 POST /api/login 401 192.168.1.5 GET /dashboard 200 172.16.0.9 GET /missing 404 Status summary: {200=2, 401=1, 404=1} Total bytes served: 6229Example 2 β Custom Template Engine with Regex Replacement
import java.util.regex.*;
import java.util.*;
public class TemplateEngine {
// Matches {{variableName}} placeholders
private static final Pattern PLACEHOLDER = Pattern.compile("\\{\\{(\\w+)\\}\\}");
public static String render(String template, Map<String, String> context) {
Matcher m = PLACEHOLDER.matcher(template);
StringBuffer sb = new StringBuffer();
while (m.find()) {
String key = m.group(1); // captured variable name
String replacement = context.getOrDefault(key, "{{" + key + "}}");
m.appendReplacement(sb, Matcher.quoteReplacement(replacement));
}
m.appendTail(sb);
return sb.toString();
}
public static void main(String[] args) {
String emailTemplate =
"Dear {{name}},\n" +
"Your order #{{orderId}} of βΉ{{amount}} has been confirmed.\n" +
"Expected delivery: {{deliveryDate}}.\n" +
"Thank you for shopping with {{storeName}}!";
Map<String, String> data = new LinkedHashMap<>();
data.put("name", "Rahul Sharma");
data.put("orderId", "ORD-20260316-4821");
data.put("amount", "3,499");
data.put("deliveryDate", "19 March 2026");
data.put("storeName", "TechStore India");
System.out.println(render(emailTemplate, data));
}
}Output
Dear Rahul Sharma, Your order #ORD-20260316-4821 of βΉ3,499 has been confirmed. Expected delivery: 19 March 2026. Thank you for shopping with TechStore India!Practice This Code β Live Editor
Advantages and Disadvantages of Regular Expressions
Regular expressions are one of the most powerful text-processing tools available. Like any powerful tool, they can be misused. Understanding both sides helps you decide when regex is the right choice.
Java RegEx β Interview Questions
Java regex is tested in interviews both as standalone knowledge and as part of string processing problems. Master every question below.
Practice Questions β Test Your Knowledge
Test your understanding of Java Regular Expressions with these practice questions. Attempt each answer before revealing it.
1. What is the difference between 'hello'.matches('hello') and Pattern.compile('hello').matcher('hello').find()? Will both return true?
Easy2. Write a regex to validate an Indian mobile number: starts with 6, 7, 8, or 9, followed by exactly 9 more digits.
Easy3. What will the following code print? String s = "aabbaabb"; System.out.println(s.replaceAll("(.)\\1", "X"));
Medium4. How would you extract all words between double quotes from a string? Write the pattern.
Medium5. What is catastrophic backtracking? Which of these patterns is vulnerable: (a) \d+ (b) (a+)+ (c) [a-z]+ (d) (.*)@(.*)
Hard6. Write a regex to split a string by either comma, semicolon, or pipe character, with optional surrounding whitespace.
Easy7. How do you write a regex that matches a word only if it is NOT preceded by 'Mr.' or 'Ms.'? Give an example.
Hard8. Write a Java method that masks all but the last 4 digits of a credit card number string. Input: '4111 1111 1111 1111', Output: '**** **** **** 1111'.
HardConclusion β Regex: The Swiss Army Knife of Text Processing
Java Regular Expressions are one of those topics that separate junior developers from senior ones β not because the syntax is complex, but because knowing when, how, and how not to use them requires experience. A well-crafted regex with named groups, proper anchors, and a pre-compiled Pattern is elegant and efficient. A poorly written regex with nested quantifiers is a security vulnerability waiting to be exploited.
The mental model is this: Pattern is the compiled rulebook β build it once, share it widely. Matcher is the inspector β create one per input, apply the rules. Always anchor validation patterns. Prefer ?: over () when you don't need to capture. And reach for a parser β not regex β when facing nested or hierarchical data.
Your next steps: practice writing regex on regex101.com (set flavor to Java 8) for instant visual feedback, study the Java NIO Files API for regex-based file scanning, and explore Spring's AntPathMatcher and PathPattern to see how regex principles apply to URL routing in enterprise applications. β