Regex Character Classes, Quantifiers and Metacharacters
A regular expression is built from a few kinds of pieces, much like a sentence is built from words and punctuation. Character classes say which characters are allowed at a spot, quantifiers say how many times, anchors say where (start, end, word boundary), and a handful of metacharacters have special meanings. Once you know these pieces, you can read almost any pattern, piece by piece.
Reading a Pattern Piece by Piece
import java.util.regex.Pattern;
public class Pieces {
static void test(String regex, String... inputs) {
Pattern p = Pattern.compile(regex);
StringBuilder line = new StringBuilder(String.format("%-14s", regex));
for (String in : inputs) {
line.append(in).append('=')
.append(p.matcher(in).matches() ? "yes" : "no").append(" ");
}
System.out.println(line.toString().strip());
}
public static void main(String[] args) {
test("[aeiou]", "a", "b");
test("[^0-9]", "x", "7");
test("[A-Z][a-z]+", "Navika", "navika", "N");
test("\\d{3}-\\d{4}", "555-1234", "55-1234");
test("files?", "file", "files", "filess");
test("ab*c", "ac", "abbbc", "adc");
test("a.c", "abc", "a c", "ac");
test("1\\.5", "1.5", "125");
test("\\d{2,4}", "7", "42", "2026", "12345");
}
}[aeiou] a=yes b=no
[^0-9] x=yes 7=no
[A-Z][a-z]+ Navika=yes navika=no N=no
\d{3}-\d{4} 555-1234=yes 55-1234=no
files? file=yes files=yes filess=no
ab*c ac=yes abbbc=yes adc=no
a.c abc=yes a c=yes ac=no
1\.5 1.5=yes 125=no
\d{2,4} 7=no 42=yes 2026=yes 12345=noEach line tests one pattern against a few inputs using matches(), so the whole input must fit. Read the tables below, then come back and check each line.
Character Classes: Which Characters
| Pattern | Matches one character that is... | Example |
|---|---|---|
[aeiou] | Any of the listed characters | a vowel |
[a-z], [A-Z], [0-9] | Any in the range | a small letter, a capital, a digit |
[A-Za-z0-9] | Any of several ranges | a letter or digit |
[^0-9] | Not in the list (^ inside brackets means "not") | anything except a digit |
\d | A digit, same as [0-9] | 7 |
\w | A "word" character: letter, digit or underscore | N, 5, _ |
\s | White space: space, tab, newline | ' ' |
\D, \W, \S | The opposite of \d, \w, \s | |
. | Any character except a newline | a, !, ' ' |
Quantifiers: How Many Times
A quantifier applies to the piece just before it:
| Quantifier | Meaning | Example |
|---|---|---|
? | 0 or 1 time (optional) | files? matches "file" and "files" |
* | 0 or more times | ab*c matches "ac" and "abbbc" |
+ | 1 or more times | [a-z]+ needs at least one letter |
{n} | Exactly n times | \d{6} is a PIN code |
{n,} | n or more times | [a-z]{2,} |
{n,m} | Between n and m times | \d{2,4} |
Anchors: Where
| Anchor | Meaning |
|---|---|
^ | Start of the text (outside brackets) |
$ | End of the text |
\b | A word boundary: between a word character and a non-word character |
"cat concat".replaceAll("\\bcat\\b", "dog") gives "dog concat": only the whole word "cat" is replaced, not the "cat" inside "concat". With matches(), ^ and $ are implied, because the whole text must match anyway.
Metacharacters and Escaping
These characters have special meanings: . * + ? ^ $ | ( ) [ ] { } \. To match one literally, put a backslash before it: \. matches a real dot. In Java source, that backslash must itself be escaped, so a real dot is written "\\." and a real backslash is "\\\\".
| You want to match | Pattern | Java string |
|---|---|---|
| A digit | \d | "\\d" |
| A literal dot | \. | "\\." |
A literal + | \+ | "\\+" |
a or b (alternation) | a|b | "a|b" |
Greedy vs Lazy
Quantifiers are greedy: they take as much as they can. Adding ? after one makes it lazy: it takes as little as it can.
public class Greedy {
public static void main(String[] args) {
String html = "<b>bold</b><b>more</b>";
System.out.println(html.replaceAll("<b>(.*)</b>", "[$1]"));
System.out.println(html.replaceAll("<b>(.*?)</b>", "[$1]"));
}
}[bold</b><b>more]
[bold][more]The greedy .* ran all the way to the last </b>, swallowing both tags in one match. The lazy .*? stopped at the first </b>, so each tag was matched separately.
Common Mistakes
Forgetting that . means "any character". Escape it to match a real dot.
Using a greedy .* between two markers. It usually grabs too much; use .*? or a negated class like [^<]*.
Confusing ^ at the start of a pattern with ^ inside brackets. Outside it means "start of text"; inside [...] it means "not".
Interview Corner
What is the difference between greedy and lazy quantifiers?
A greedy quantifier such as * or + matches as many characters as possible and then backtracks if needed; a lazy quantifier such as *? or +? matches as few as possible and expands only if needed. For <b>.*</b> on text with two bold tags, greedy matches from the first <b> to the last </b>, while <b>.*?</b> matches each tag separately.
Practice
- Easy: Write a pattern for an Indian vehicle number like
TS09AB1234: two capital letters, two digits, one or two capital letters, four digits. Test it on three inputs. - Harder: Write a pattern that matches a time in 24-hour format,
00:00to23:59, and rejects24:00and9:5.
Key Takeaways
Was this tutorial helpful?