Regular Expressions

Su Suriya Ravichandran Updated 14 Nov 2026
6 min read ·Lesson 103 of 133

A regular expression (regex) is a pattern that describes a set of strings: "six digits," "an email address," "a date like 2026-11-12," "any run of whitespace." Java uses regular expressions to validate input, find and extract parts of text, replace matches, and split strings. The core classes are Pattern and Matcher in java.util.regex; String.matches, replaceAll, replaceFirst, and split are convenient shortcuts built on them.

A First Example

boolean valid = "600042".matches("\\d{6}");       // true: exactly six digits
boolean invalid = "6000AB".matches("\\d{6}");      // false

\d means "a digit" and {6} means "exactly six times." Inside a Java string literal, every backslash must be doubled, so the regex \d{6} is written "0d{6}".

Pattern Syntax

Characters and classes

PatternMatches
abcThe literal text abc
.Any single character (except a line break, by default)
[abc]One of a, b, or c
[a-z], [A-Z0-9]A character in a range
[^0-9]Any character not listed
\d / \DA digit / a non-digit
\w / \WA word character (letter, digit, underscore) / anything else
\s / \SWhitespace / non-whitespace
0, 1, 2A literal ., +, ( (escaped metacharacter)

The metacharacters . ^ $ | ? * + ( ) [ ] { } 0 have special meanings; escape them with ` (written 2 in Java source) to match them literally, or wrap literal text in Pattern.quote(...).

Quantifiers

QuantifierMeaning
*0 or more
+1 or more
?0 or 1 (optional)
{n}Exactly n
{n,}n or more
{n,m}Between n and m

Quantifiers are greedy: they match as much as possible. Adding ? makes them lazy, matching as little as possible:

"<b>x</b><b>y</b>".replaceAll("<b>.*</b>", "[]");    // "[]"     greedy: one match spanning both
"<b>x</b><b>y</b>".replaceAll("<b>.*?</b>", "[]");   // "[][]"   lazy: two separate matches

Anchors and alternation

PatternMeaning
^ / $Start / end of the input (or of a line in MULTILINE mode)
\bA word boundary
a|bEither a or b

Groups

PatternMeaning
(...)A capturing group: matched text is available as group(1), group(2), ...
(?<name>...)A named capturing group: available as group("name")
(?:...)A non-capturing group: groups for quantifiers or alternation without capturing
(?=...), (?!...)Lookahead: what follows must (or must not) match, without consuming it
(?<=...), (?<!...)Lookbehind: what precedes must (or must not) match

Pattern and Matcher

For anything beyond a one-off check, compile the pattern once and reuse it:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

Pattern pin = Pattern.compile("[1-9]\\d{5}");     // Indian PIN code: six digits, no leading zero

Matcher m = pin.matcher("600042");
System.out.println(m.matches());                   // true

Pattern.compile parses the regex into an efficient internal form. A compiled Pattern is immutable and thread-safe, so it is normally stored in a static final field. A Matcher holds the state of one search over one input and is not thread-safe; create a new one for each input.

String.matches(regex) is equivalent to Pattern.compile(regex).matcher(s).matches(), which means it recompiles the pattern on every call. Avoid it inside loops.

matches, find, and lookingAt

MethodSucceeds if
matches()The entire input matches the pattern
lookingAt()The beginning of the input matches
find()The pattern occurs anywhere; each call finds the next occurrence
Pattern digits = Pattern.compile("\\d+");
digits.matcher("order 42").matches();    // false: the whole string is not digits
digits.matcher("order 42").find();       // true:  "42" occurs in it

Validation uses matches; extraction uses a find loop.

Extracting matches and groups

Matcher m = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})").matcher("Due 2026-11-12, paid 2026-11-10");
while (m.find()) {
    System.out.println(m.group() + " -> year " + m.group(1) + ", month " + m.group(2));
}
// 2026-11-12 -> year 2026, month 11
// 2026-11-10 -> year 2026, month 10

group() (or group(0)) is the whole match; group(n) is the text captured by the nth parenthesis. start() and end() give its position. Since Java 9, results() returns all matches as a stream:

List<String> numbers = Pattern.compile("\\d+").matcher("a1b22").results()
        .map(MatchResult::group)
        .toList();                       // [1, 22]

Flags

Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
p.matcher("I love JAVA").find();         // true

Pattern q = Pattern.compile("(?i)java");  // the same flag written inline
FlagInlineEffect
CASE_INSENSITIVE(?i)Ignore case
MULTILINE(?m)^ and $ match at each line start and end
DOTALL(?s). also matches line breaks
COMMENTS(?x)Ignore whitespace and allow # comments in the pattern
UNICODE_CHARACTER_CLASS(?U)\w, \d, etc. match Unicode letters and digits, not only ASCII

Replacing and Splitting

"2026-11-12".replaceAll("(\\d+)-(\\d+)-(\\d+)", "$3.$2.$1");   // "12.11.2026"
"a, b ;c".split("\\s*[,;]\\s*");                               // [a, b, c]

In replacements, $n inserts group n, so a literal $ must be escaped (or the replacement wrapped with Matcher.quoteReplacement). See the String Replace and String Split lessons.

Performance and Safety

  • Compile once. Store patterns in static final fields.
  • Prefer simple methods for fixed text. contains, indexOf, and replace are faster and clearer when no pattern is needed.
  • Beware catastrophic backtracking. Nested quantifiers such as (a+)+$ can take exponential time on certain inputs. A regex applied to user-supplied text can then be used to freeze a server, an attack known as ReDoS. Keep patterns specific, avoid nested repetition, and limit input length before matching.
  • Know when to stop. Regexes are good for flat, well-defined formats. For HTML, JSON, or full email address validation, use a proper parser or library.

Practical Example: Validating and Extracting Data

import java.util.*;
import java.util.regex.*;

public class Rx {
    static final Pattern MOBILE = Pattern.compile("(?:\\+91)?([6-9]\\d{9})");
    static final Pattern PINCODE = Pattern.compile("[1-9]\\d{5}");
    static final Pattern ORDER_LINE = Pattern.compile(
            "(?<date>\\d{4}-\\d{2}-\\d{2})\\s+(?<id>ORD-\\d+)\\s+(?<amount>\\d+(?:\\.\\d{1,2})?)\\s+(?<status>PAID|FAILED|REFUNDED)");

    public static void main(String[] args) {
        for (String m : List.of("9876543210", "+91 98765 43210", "+91-9876543210", "5876543210", "98765")) {
            Matcher mm = MOBILE.matcher(m.replaceAll("[\\s-]", ""));
            System.out.printf("%-16s %s%n", m, mm.matches() ? "valid -> " + mm.group(1) : "invalid");
        }
        System.out.println("600042 " + PINCODE.matcher("600042").matches() + ", 060042 " + PINCODE.matcher("060042").matches());

        String report = """
            2026-11-01  ORD-1001  1499.00  PAID
            2026-11-01  ORD-1002  250     FAILED
            garbage line
            2026-11-02  ORD-1003  3200.5   PAID
            2026-11-03  ORD-1004  899.99   REFUNDED
            """;
        double paid = 0;
        Matcher m = ORDER_LINE.matcher(report);
        while (m.find()) {
            if (m.group("status").equals("PAID")) {
                paid += Double.parseDouble(m.group("amount"));
            }
            System.out.println(m.group("id") + " " + m.group("status") + " on " + m.group("date"));
        }
        System.out.printf("Paid total: %.2f%n", paid);

        String text = "Contact ada@gocourse.in or support@gocosys.com; not a@b";
        Matcher em = Pattern.compile("[\\w.+-]+@[\\w-]+(\\.[\\w-]+)+").matcher(text);
        List<String> emails = new ArrayList<>();
        while (em.find()) emails.add(em.group());
        System.out.println(emails);
    }
}

Output:

9876543210       valid -> 9876543210
+91 98765 43210  valid -> 9876543210
+91-9876543210   valid -> 9876543210
5876543210       invalid
98765            invalid
600042 true, 060042 false
ORD-1001 PAID on 2026-11-01
ORD-1002 FAILED on 2026-11-01
ORD-1003 PAID on 2026-11-02
ORD-1004 REFUNDED on 2026-11-03
Paid total: 4699.50
[ada@gocourse.in, support@gocosys.com]

The program does three things:

  • Validates mobile numbers. The number is first normalized by removing spaces and hyphens with a small regex. MOBILE then makes the +91 prefix optional with a non-capturing group (?:...)?, requires a first digit of 6–9, and captures the ten-digit number in group 1, so all three formatting styles produce the same normalized result. The PIN code pattern rejects a leading zero with [1-9].
  • Parses report lines. ORDER_LINE uses named groups, so the code reads m.group("amount") instead of a position number. Its amount part \d+(?:0\d{1,2})? accepts whole numbers and up to two decimals. The find() loop picks out every valid line and silently skips the garbage line, because find searches for matches rather than requiring the whole input to match.
  • Extracts email addresses. The email pattern pulls addresses out of free text. It requires at least one dot after the @, so a@b is ignored. It is deliberately simple; fully validating email syntax is better left to a library or to sending a confirmation email.

All three patterns are compiled once into static final fields.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Regular Expressions and get answers drawn from it.

Signed-in readers only.