Skip to content

Java Regular Expressions

Regular expressions (regex) are sequences of characters that define a search pattern. They are powerful tools for pattern matching, searching, and manipulating text.

Java provides the java.util.regex package for working with regular expressions. Its syntax is largely compatible with Perl.

  • Pattern Class: Represents a compiled regular expression. Compiling a pattern is often the first step and can be computationally intensive, so it’s efficient to compile patterns once if they are reused. Pattern objects are immutable and thread-safe. You create a Pattern using the static compile() method: Pattern p = Pattern.compile(regexString);
  • Matcher Class: An engine that performs matching operations on an input character sequence using a Pattern. A Matcher is obtained by calling the matcher(input) method on a Pattern object: Matcher m = p.matcher(inputString);. Matcher objects are stateful and not thread-safe.
  • PatternSyntaxException: An unchecked exception thrown to indicate a syntax error in a regular expression pattern during compilation.
  1. Define your regular expression as a String.
  2. Compile the regex String into a Pattern object using Pattern.compile().
  3. Create a Matcher object from the Pattern and your input text using pattern.matcher(inputText).
  4. Use Matcher methods (like find(), matches(), lookingAt(), group()) to perform matching operations and retrieve results.

Parentheses () in a regex create capturing groups. They allow you to:

  • Treat multiple characters as a single unit (e.g., for applying quantifiers like *, +, ?).
  • Extract specific parts of the matched text.

Groups are numbered starting from 1 based on their opening parenthesis from left to right. Group 0 always represents the entire match.

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexGroupsDemo {
public static void main(String[] args) {
String text = "User: alice ID: 101, User: bob ID: 205";
// Regex to find 'User: (name)' and 'ID: (number)'
// (\w+) captures the username (word characters)
// (\d+) captures the ID (digits)
String regex = "User: (\\w+) ID: (\\d+)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
// Use find() to locate matches within the text
while (matcher.find()) {
System.out.println("Full match (Group 0): " + matcher.group(0));
System.out.println("Username (Group 1): " + matcher.group(1));
System.out.println("User ID (Group 2): " + matcher.group(2));
System.out.println("Start index: " + matcher.start() + ", End index: " + matcher.end());
System.out.println("---");
}
}
}

Expected Output:

Full match (Group 0): User: alice ID: 101
Username (Group 1): alice
User ID (Group 2): 101
Start index: 0, End index: 19
---
Full match (Group 0): User: bob ID: 205
Username (Group 1): bob
User ID (Group 2): 205
Start index: 21, End index: 38
---

You can find the number of capturing groups using matcher.groupCount() (which excludes group 0).

Regular Expression Syntax Quick Reference:

Section titled “Regular Expression Syntax Quick Reference:”

Common metacharacters and constructs:

SyntaxDescription
.Any single character (except newline unless DOTALL flag is set)
^Beginning of the line (or string with MULTILINE flag)
$End of the line (or string with MULTILINE flag)
[...]Character class: Matches any single character inside the brackets (e.g., [abc])
[^...]Negated character class: Matches any single character not inside (e.g., [^0-9])
A|BAlternation: Matches either A or B
(...)Capturing group
(?:...)Non-capturing group (groups characters but doesn’t capture)
\dA digit: [0-9]
\DA non-digit: [^0-9]
\sA whitespace character: [ \t\n\x0B\f\r]
\SA non-whitespace character
\wA word character: [a-zA-Z_0-9]
\WA non-word character
\bWord boundary
\BNon-word boundary
*Quantifier: Zero or more occurrences of the preceding element
+Quantifier: One or more occurrences
?Quantifier: Zero or one occurrence
{n}Quantifier: Exactly n occurrences
{n,}Quantifier: At least n occurrences
{n,m}Quantifier: At least n but not more than m occurrences
*?, +?, ??, {n,}? etc.Lazy/Reluctant quantifiers: Match the shortest possible string

Note: Backslashes (\) need to be escaped within Java String literals (e.g., \d becomes "\\d").

MethodDescription
boolean find()Attempts to find the next subsequence of the input that matches the pattern. Returns true if found, false otherwise. Often used in a while loop.
boolean find(int start)Resets the matcher and searches starting from the specified index.
boolean matches()Attempts to match the entire input sequence against the pattern. Returns true only if the whole input matches.
boolean lookingAt()Attempts to match the pattern starting from the beginning of the input sequence. Doesn’t require the entire input to match.
MethodDescription
String group() or String group(0)Returns the input subsequence matched by the previous match (the entire match).
String group(int group)Returns the subsequence captured by the given group number during the previous match.
String group(String name)Returns the subsequence captured by the given named group (?<name>...).
int groupCount()Returns the number of capturing groups in the pattern (excluding group 0).
int start() or int start(0)Returns the start index of the previous match.
int start(int group)Returns the start index of the subsequence captured by the given group.
int end() or int end(0)Returns the offset after the last character of the previous match.
int end(int group)Returns the offset after the last character captured by the given group.
MethodDescription
String replaceAll(String replacement)Replaces every subsequence of the input that matches the pattern with the replacement string.
String replaceFirst(String replacement)Replaces only the first subsequence that matches the pattern.
Matcher appendReplacement(StringBuffer sb, String replacement) or Matcher appendReplacement(StringBuilder sb, String replacement)Appends the text preceding the current match and the replacement text to the given buffer. Allows complex replacements, including using captured groups ($1, $2, etc.) in the replacement string.
StringBuffer appendTail(StringBuffer sb) or StringBuilder appendTail(StringBuilder sb)Appends the remainder of the input sequence (after the last match) to the buffer. Typically used after a loop with appendReplacement.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexReplaceDemo {
private static final String REGEX = "\b(cat|dog|fish)\b"; // Match whole words: cat, dog, or fish
private static final String INPUT = "The quick brown fox jumps over the lazy dog. The cat naps.";
private static final String REPLACE_ALL = "animal";
private static final String REPLACE_FIRST = "pet";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
Matcher m = p.matcher(INPUT);
// Replace all occurrences
String resultAll = m.replaceAll(REPLACE_ALL);
System.out.println("Replace All: " + resultAll);
// Replace only the first occurrence (need a new matcher or reset)
m.reset(); // Reset the matcher to the beginning of the input
String resultFirst = m.replaceFirst(REPLACE_FIRST);
System.out.println("Replace First: " + resultFirst);
}
}

Expected Output:

Replace All: The quick brown fox jumps over the lazy animal. The animal naps.
Replace First: The quick brown fox jumps over the lazy pet. The cat naps.

Example: appendReplacement and appendTail (Using StringBuilder)

Section titled “Example: appendReplacement and appendTail (Using StringBuilder)”
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexAppendDemo {
private static final String REGEX = "(John|Jane)";
private static final String INPUT = "John Doe met Jane Smith. John works hard.";
private static final String REPLACE_FORMAT = "**$1**"; // $1 refers to the first captured group
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
Matcher m = p.matcher(INPUT);
StringBuilder sb = new StringBuilder();
while (m.find()) {
// Append text before the match and the replacement
m.appendReplacement(sb, REPLACE_FORMAT);
}
// Append any remaining text after the last match
m.appendTail(sb);
System.out.println(sb.toString());
}
}

Expected Output:

**John** Doe met **Jane** Smith. **John** works hard.
  • Compile Once, Reuse: Compiling a Pattern is expensive. If you use the same regex multiple times, compile it once and reuse the Pattern object.
  • Avoid Complex Regex in Hot Loops: Very complex patterns or inefficiently written regex can significantly impact performance.
  • Consider Alternatives: For simple substring searching or splitting, standard String methods (contains(), indexOf(), split()) might be faster than regex.