Java Regular Expressions
Table of Contents + −
In the last lesson you learned about Java immutable strings. Now picture real jobs like these:
- Check that text typed into a form looks like an email before you save it.
- Pull every phone number out of a long block of text.
- Split a line cleanly when it has one space here and three spaces there.
Character-by-character loops handle all of this, but they are slow to write and easy to get wrong. A regular expression (“regex” for short) is a much sharper tool.
🤔 What is a regex?
A regular expression is a small pattern that describes the shape of text. You do not list every allowed value. You describe the shape, then Java checks real text against it.
- It works like a search filter: not “find the word cat” but “find any three letters”.
- The pattern is the rule. The text either fits or it does not.
The String method matches asks “does this whole String fit this pattern?” and hands back true or false.
String text = "hello";
System.out.println(text.matches("hello")); // exact textSystem.out.println(text.matches("world")); // does not fit- A plain pattern with no special characters matches that exact text only.
- The power comes from the special building blocks, which we add step by step.
Output
truefalse⚠️ The double backslash rule
Know one Java rule first, or every example will confuse you:
- Many regex blocks start with a backslash, like
\dfor “a digit”. - In a Java String the backslash is already special, so you write two to get one.
- So the digit pattern is
"\\d"in Java code: the first backslash escapes, the second reaches the regex.
This example checks for one digit.
String text = "7";
System.out.println(text.matches("\\d")); // ✅ correct, one backslash reaches the regexEvery backslash you read in a regex, you type twice in Java. Forget the second one and the code will not compile or will behave strangely.
Two backslashes, always
In regex theory you read \d, \w, \s. In Java code you write "\\d", "\\w", "\\s". The doubling is a Java string rule, not a regex rule. This trips up almost everyone at the start.
🔤 Character classes
A character class is a set of characters in square brackets. It matches any one character from the set.
[abc]matches a singlea,b, orc.- A dash makes a range:
[a-z]is any lowercase letter,[0-9]is any digit.
This example checks single characters against a few classes.
System.out.println("b".matches("[abc]")); // b is in the setSystem.out.println("d".matches("[abc]")); // d is not in the setSystem.out.println("k".matches("[a-z]")); // any lowercase letterSystem.out.println("5".matches("[0-9]")); // any digitSystem.out.println("X".matches("[a-zA-Z]")); // any letter, either caseReading the results:
[abc]matchesbbut notd, becausedis not listed.[a-z]matchesk, since it falls in the range.[0-9]matches5, a digit.[a-zA-Z]matchesX, because you can combine ranges in one class.- A
^as the first character inside flips the set:[^0-9]matches anything that is not a digit.
Output
truefalsetruetruetrue🔢 Shorthand classes: digits, words, whitespace
Common classes get short names that save typing:
\dis[0-9], any single digit.\wis a “word” character: a letter, a digit, or an underscore.\sis any whitespace: a space, a tab, or a newline.- The capital version means the opposite:
\Dnon-digit,\Wnon-word,\Snon-whitespace.
This example uses the shorthands. In Java each one needs two backslashes.
System.out.println("4".matches("\\d")); // a digitSystem.out.println("a".matches("\\w")); // a word characterSystem.out.println(" ".matches("\\s")); // a spaceSystem.out.println("a".matches("\\D")); // a non-digit, true"4"fits\d,"a"fits\w, a space fits\s."a"fits\Dbecause a letter is not a digit.- These three cover a huge share of real patterns, so memorise them.
Output
truetruetruetrue🔁 Quantifiers: how many times
So far each pattern matched one character. A quantifier says how many times the thing before it may repeat.
*means zero or more times.+means one or more times.?means zero or one time, so optional.{n}means exactly n times, and{n,m}means between n and m times.
This example matches whole groups of characters.
System.out.println("12345".matches("\\d+")); // one or more digitsSystem.out.println("".matches("\\d*")); // zero or more, empty fitsSystem.out.println("color".matches("colou?r")); // the u is optionalSystem.out.println("1234".matches("\\d{4}")); // exactly four digitsSystem.out.println("12".matches("\\d{2,4}")); // between two and fourWalking through it:
\d+matches"12345", one or more digits in a row.\d*matches even the empty String"", since “zero or more” allows zero.colou?rmatches both"color"and"colour", because?makes theuoptional.\d{4}matches exactly four digits, good for a year or a PIN.\d{2,4}matches a run of two, three, or four digits.- A quantifier attaches to the thing right before it:
\d+repeats the digit,(ab)+repeats the pair.
Output
truetruetruetruetrue⚓ Anchors: start and end
Anchors do not match a character. They match a position.
^means the start of the text,$means the end.- They matter most when you search inside a larger block with
Matcher. - With String
matchesthe pattern must fit the whole String anyway, so anchors are less needed there.
This example uses an anchor with a search, not a full match.
String text = "cat catalog scatter";
// ^cat means a "cat" that sits at the very startSystem.out.println(text.replaceAll("^cat", "DOG"));^catmatches only thecatat the very start, not the ones insidecatalogorscatter.- So only the first word changes; without
^, everycatwould be replaced.
Output
DOG catalog scatter🧱 Groups and alternation
A group is a pattern wrapped in round brackets ( ). It does two jobs:
- Lets a quantifier repeat a whole chunk.
- Lets you pull that chunk out later.
- Alternation
|means “or”:cat|dogmatches eithercatordog.
This example shows both.
System.out.println("abab".matches("(ab)+")); // repeat the whole pairSystem.out.println("cat".matches("cat|dog")); // cat or dogSystem.out.println("dog".matches("cat|dog")); // dog also fitsSystem.out.println("yesyesyes".matches("(yes){3}"));// exactly three "yes"Reading it:
(ab)+matches"abab", the groupabrepeating one or more times.cat|dogmatches both"cat"and"dog", since either side of|is allowed.(yes){3}matches"yesyesyes", the whole group exactly three times.- Groups shine when you want to extract part of the text, which we do with Matcher next.
Output
truetruetruetrue✂️ Escaping special characters
Some characters have special meaning in regex: ., *, +, ?, (, ), [, ], |, \.
- The dot
.on its own means “any single character”, not a literal full stop. - To match a real dot, escape it with a backslash:
\.. - Since this is Java, that backslash becomes two:
"\\.".
This example shows the difference.
System.out.println("a".matches(".")); // dot means any characterSystem.out.println("ab".matches("a.b")); // any character in the middleSystem.out.println("a.b".matches("a\\.b")); // ✅ escaped dot, a real dot onlySystem.out.println("axb".matches("a\\.b")); // false, x is not a real dot.matches any one character, so"a"fits it anda.bmatchesaxb.- Escaped as
\.("\\."in Java), it matches a real dot only. - This is why splitting a String on a dot needs
"\\.", not just".".
Output
truetruetruefalse🛠️ The String regex shortcuts
The String class has four methods that take a regex. For quick one-off jobs these are all you need.
matches(regex)returns true only if the whole String fits the pattern.replaceAll(regex, replacement)swaps every match for the replacement text.replaceFirst(regex, replacement)swaps only the first match.split(regex)breaks the String into an array wherever the pattern matches.
This example uses all four.
String text = "Order 42 shipped, order 99 pending";
System.out.println("abc123".matches("\\w+")); // all word charsSystem.out.println(text.replaceAll("\\d+", "#")); // every numberSystem.out.println(text.replaceFirst("\\d+", "#")); // only the firstSystem.out.println("a b c".split("\\s+").length); // split on spacesWhat each line does:
matches("\\w+")is true because"abc123"is all word characters.replaceAll("\\d+", "#")turns every run of digits into a#.replaceFirst("\\d+", "#")changes only the first number, leaving99alone.split("\\s+")breaks on one or more spaces, so messy gaps collapse into three pieces.- Plain
split(" ")would leave empty strings between extra spaces;\s+avoids that.
Output
trueOrder # shipped, order # pendingOrder # shipped, order 99 pending3🧰 Pattern and Matcher for reuse and searching
The String shortcuts are fine for a one-time check, but they have two limits:
- They rebuild the pattern every call, which is wasteful in a loop.
- They cannot easily find and loop over many matches inside a longer text.
For both jobs use Pattern and Matcher. Compile the pattern once into a Pattern, then ask it for a Matcher on some text, and the Matcher does the searching.
This example compiles a pattern once, then reuses it.
import java.util.regex.Pattern;import java.util.regex.Matcher;
public class Main { public static void main(String[] args) { Pattern p = Pattern.compile("\\d+"); // compile once Matcher m = p.matcher("a1b22c333"); // point it at some text
while (m.find()) { // find each match in turn System.out.println(m.group()); // the matched text } }}Here is the flow:
Pattern.compile("\\d+")builds the pattern object once.p.matcher(...)creates a Matcher aimed at the text to search.m.find()moves to the next match and returns true while one remains.m.group()hands back the text the lastfind()matched.- So the loop prints every run of digits, the standard way to pull pieces from a larger String.
Output
122333🎯 Capturing groups with Matcher
When you search with a Matcher, each group gets a number:
group(0)is the whole match.group(1)is the first bracketed group,group(2)the second, and so on.- This pulls out exactly the part you care about.
This example reads a date and pulls the day, month, and year apart.
import java.util.regex.Pattern;import java.util.regex.Matcher;
public class Main { public static void main(String[] args) { Pattern p = Pattern.compile("(\\d{2})/(\\d{2})/(\\d{4})"); Matcher m = p.matcher("Today is 13/06/2026 here");
if (m.find()) { System.out.println("Whole: " + m.group(0)); System.out.println("Day: " + m.group(1)); System.out.println("Month: " + m.group(2)); System.out.println("Year: " + m.group(3)); } }}- The pattern has three groups, one per part of the date.
- After
find(),group(0)is the full13/06/2026;group(1)–group(3)give the three parts. - This is far cleaner than chopping the String up by hand with
substring.
Output
Whole: 13/06/2026Day: 13Month: 06Year: 2026📧 Practical example: validate an email
Let’s check that a String looks like a basic email. The shape: some word-ish characters, an @, a domain, a dot, then an ending like com or org.
Here is a simple pattern that covers most everyday emails.
public class Main { public static void main(String[] args) { String pattern = "[\\w.]+@[\\w.]+\\.[a-z]{2,}";
System.out.println("alex@example.com".matches(pattern)); // valid System.out.println("riya.k@mail.co".matches(pattern)); // valid System.out.println("not-an-email".matches(pattern)); // no @ System.out.println("a@b".matches(pattern)); // no .ending }}Reading the pattern piece by piece:
[\w.]+is one or more word characters or dots, the name part.@is a literal@.[\w.]+is the domain.\.is a real, escaped dot.[a-z]{2,}is an ending of at least two letters, likecom.- So a clean address fits and the broken ones do not.
Real email rules are surprisingly complex. This is a friendly “good enough” version, not the full official rule, and it works well for most forms.
Output
truetruefalsefalse📞 Practical example: validate a phone and extract numbers
Two quick tasks: check that a phone number is exactly ten digits, and pull every number out of a sentence.
This example does both.
import java.util.regex.Pattern;import java.util.regex.Matcher;
public class Main { public static void main(String[] args) { // a phone is exactly 10 digits System.out.println("9876543210".matches("\\d{10}")); // true System.out.println("98765".matches("\\d{10}")); // too short
// pull every number out of a sentence Pattern p = Pattern.compile("\\d+"); Matcher m = p.matcher("Bought 3 pens and 12 books for 250 rupees"); while (m.find()) { System.out.println(m.group()); } }}\d{10}means exactly ten digits and nothing else, so a full number passes and a short one fails.\d+plus afind()loop pulls out each run of digits in order.- This is the same Matcher pattern you saw before, now doing real work.
Output
truefalse312250🪓 Practical example: split on messy spaces
Real-world data is rarely tidy. A line might have one space here and several there.
- Split on a single space and you get empty pieces.
- Split on
\s+(“one or more whitespace”) and runs of spaces collapse into one separator.
This example compares the two ways.
public class Main { public static void main(String[] args) { String messy = "Alex Riya Sam";
String[] bad = messy.split(" "); // ❌ extra spaces make empty pieces String[] good = messy.split("\\s+"); // ✅ collapses runs of spaces
System.out.println("Bad count: " + bad.length); System.out.println("Good count: " + good.length); for (String name : good) { System.out.println(name); } }}- Splitting on a single space counts gaps wrong: each extra space creates an empty entry.
- Splitting on
\s+treats any run of spaces as one separator, giving the three clean names. - This one trick fixes a huge share of “why is my array the wrong size?” bugs.
Output
Bad count: 7Good count: 3AlexRiyaSam⚠️ Common Mistakes
A few regex slip-ups catch nearly everyone.
Forgetting the double backslash. Every regex backslash must be doubled in Java, or it will not compile.
String s = "5";
s.matches("\d"); // ❌ will not compile, single backslash is invalid in Javas.matches("\\d"); // ✅ two backslashes, the regex sees oneThinking matches searches inside the text. The matches method only returns true if the whole String fits the pattern, not just a part of it.
String s = "the year is 2026";
System.out.println(s.matches("\\d+")); // ❌ false, the whole String is not all digitsSystem.out.println(s.matches(".*\\d+.*")); // ✅ true, .* allows text around the digitsTo know whether a pattern appears somewhere inside, use a Matcher with find(), or wrap the pattern with .* on both sides.
Forgetting greedy versus lazy matching. Quantifiers like + and * are greedy by default, grabbing as much as they can. Add ? after them to make them lazy, grabbing as little as possible.
import java.util.regex.Pattern;import java.util.regex.Matcher;
public class Main { public static void main(String[] args) { String text = "<a><b>";
Matcher g = Pattern.compile("<.+>").matcher(text); g.find(); System.out.println(g.group()); // ❌ greedy, grabs the whole "<a><b>"
Matcher l = Pattern.compile("<.+?>").matcher(text); l.find(); System.out.println(l.group()); // ✅ lazy, stops at the first ">", gives "<a>" }}- The greedy
<.+>stretches to the last>, swallowing everything. - The lazy
<.+?>stops at the first>, usually what you want.
✅ Best Practices
Habits that keep your regex code clean and fast.
- Compile once, reuse the Pattern. If the same pattern runs many times, build it with
Pattern.compileoutside the loop instead of callingString.matchesover and over. - Use
find()to search andmatches()to validate.matcheschecks the whole String.findlooks for a match anywhere inside. - Always double your backslashes. Read
\din your head, type"\\d"in your code. - Escape characters that have meaning. A real dot is
\., a real bracket is\(, and so on, both doubled in Java. - Make quantifiers lazy when you want the shortest match. Add
?to turn+into+?and*into*?. - Keep patterns simple and readable. A pattern that is “good enough” and clear beats a perfect monster nobody can read later.
🧩 What You’ve Learned
Regex is a tool you will reach for again and again. The key ideas:
- ✅ A regex is a pattern that describes the shape of text, and
matcheschecks the whole String against it. - ✅ In Java you write every regex backslash twice, so a digit is
"\\d". - ✅ Building blocks: character classes
[a-z], shorthands\d \w \s, anchors^ $, quantifiers* + ? {n}, groups( ), and alternation|. - ✅ Escape special characters like the dot with
\.to match them literally. - ✅ The String shortcuts
matches,replaceAll,replaceFirst, andsplitcover quick jobs. - ✅ Use
Pattern.compileonce, then aMatcherwithfind()andgroup()to search a long text and pull out parts.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
How do you write the regex digit pattern in a Java String?
Why: Java needs the backslash doubled, so \d in regex is written "\\d" in Java code.
- 2
What does the String matches method check?
Why: matches returns true only when the entire String fits the pattern, not just a part of it.
- 3
Which method do you call to step through every match in a longer text?
Why: matcher.find() moves to the next match and returns true while matches remain.
- 4
What does the quantifier + mean?
Why: The + quantifier means the thing before it must appear one or more times.
🚀 What’s Next?
You can now match, search, and pull text apart with confidence. The next stop is working with dates and times, another everyday job that has its own handy classes. Let’s look at it next.