Regular Expressions

 

java.util.regex

 

 


 

Overview

java.util.regex, part of J2SE 1.4 , allows one to do regular expression pattern matching in the service of such things as searches and text manipulation. It consists of three classes:

  1. Pattern

    Compiled representation of a regular expression. Provides no public constructors. To create a pattern, you must call one of its public static compile methods, both of which will return a Pattern object. Pattern methods accept a string as their first argument.

  2. Matcher

    Engine that interprets the pattern and performs match operations against an input string. Like the Pattern class, Matcher defines no public constructors. You obtain a Matcher object by calling the public matcher method on a Pattern object.

  3. PatternSyntaxException

    Unchecked exception that indicates a syntax error in a regular expression pattern.

 


 

Test Harness

Defines a reusable test harness to explore the regular expression constructs that the regex API supports. You can run this code with the command java RegexTestHarness. No command-line arguments are accepted. At runtime, it loads the regular expression and input data from a plain text file,

Create a file, regex.txt, which resides in the same directory. This file contains a simple format: the regular expression appears on the first line, and the input string appears on the second line. To update the test, simply edit those two lines, save the file, and run the test again. There is no need to recompile any code.  You can download the test harness code, RegexTestHarness.java

The output provides some useful information including the regular expression, the input string, the matched text (if found), and the start and end indices of where the match has occurred. Here's the code:

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


private static String REGEX;
private static String INPUT;
private static BufferedReader br;
private static Pattern pattern;
private static Matcher matcher;
private static boolean found;

public static void main(String[] argv) 
{
    initResources();
    processTest();
    closeResources();
}

private static void initResources() 
{
   try {
       br = new BufferedReader(new FileReader("regex.txt"));
   }
   catch  FileNotFoundException(fnfe) 
   {
        System.out.println("Cannot locate input file! "+fnfe.getMessage());
        System.exit(0);
   }
   try 
   {
       REGEX = br.readLine();
       INPUT = br.readLine();
   } catch  IOException(ioe) {}

    pattern = Pattern.compile(REGEX);
    matcher = pattern.matcher(INPUT);

    System.out.println("Current REGEX is: "+REGEX);
    System.out.println("Current INPUT is: "+INPUT);
}

private static void processTest() 
{
    while(matcher.find()) 
    {
        System.out.println("I found the text \"" + matcher.group() +
                           "\" starting at index " + matcher.start() +
                           " and ending at index " + matcher.end() + ".");
        found = true;
    }

    if(!found)
    {
        System.out.println("No match found.");
    }
}

private static void closeResources() 
{
   try{
       br.close();
   }catch IOException(ioe){}
}
Before continuing with the next section, save and compile this code to ensure that your development environment supports the java.util.regex package.

 


 

String Literals

The most basic form of pattern matching supported by this API is the match of a string literal. For example, if the regular expression is foo, and the input string is foo, the match will succeed because these strings are identical. Try this out by creating a text file regex.txt and putting the text "foo" on lines 1 and 2. The first line is the regular expression, and the second line is the input string. Save this file in the same directory as RegexTestHarness.java. When you run this code (type java RegexTestHarness on the command line), you should get the following output:

 
Current REGEX is: foo
Current INPUT is: foo
I found the text "foo" starting at index 0 and ending at index 3.
This match was a success. Note that while the input string is 3 characters long, the start index is 0 and the end index is 3. By convention, ranges are inclusive of the beginning index and exclusive of the end index, as shown in the following figure:

Each character in the string resides in its own cell, with the index positions pointing between each cell. The string "foo" starts at index 0 and ends at index 3, even though the characters themselves only occupy cells 0, 1, and 2.

With subsequent matches, you'll notice some overlap; the start index for the next match is the same as the end index of the previous match:

 
Current REGEX is: foo
Current INPUT is: foofoofoo
I found the text "foo" starting at index 0 and ending at index 3.
I found the text "foo" starting at index 3 and ending at index 6.
I found the text "foo" starting at index 6 and ending at index 9.

Metacharacters

This API also supports a number of special characters which can affect the way a pattern is matched. In your regex.txt file, change the regular expression to cat.. and the input string to cats. Here's the result:

Current REGEX is: cat.
Current INPUT is: cats
I found the text "cats" starting at index 0 and ending at index 4.
The match still succeeds, even though the period (.) is not present in the input string. It succeeds because the period is a metacharacter--a character with special meaning interpreted by the matcher. The metacharacter "." means "any character" which is why the match in our example succeeds.

The metacharacters supported by this API are: ([{\^$|)?*+.  In certain situations the special characters listed above will not be treated as metacharacters. You'll encounter this as you learn more about how regular expressions are constructed. You can, however, use this list to check whether or not a specific character will ever be considered a metacharacter. For example, the characters ! @ and # never carry a special meaning.

There are two ways to force a metacharacter to be treated as an ordinary character:

  • precede the metacharacter with a blackslash, or
  • enclose it within \Q (which starts the quote) and \E (which ends it).
When using this technique, the \Q and \E can be placed at any location within the expression, provided that the \Q comes first.

 


 

Character Classes

If you browse through the Pattern class specification, you'll see tables summarizing the supported regular expression constructs. In the "Character classes" section you'll find the following:

Character Classes
[abc] a, b, or c (simple class)
[^abc] Any character except a, b, or c (negation)
[a-zA-Z] a through z, or A through Z, inclusive (range)
[a-d[m-p]] a through d, or m through p: [a-dm-p] (union)
[a-z&&[def]] d, e, or f (intersection)
[a-z&&[^bc]] a through z, except for b and c: [ad-z] (subtraction)
[a-z&&[^m-p]] a through z, and not m through p: [a-lq-z] (subtraction)
The left-hand column specifies the regular expression constructs, while the right-hand column describes the conditions under which each construct will match.  The word "class" in the phrase "character class" does not refer to a .class file. In the context of regular expressions, a character class is a set of characters enclosed within square brackets. It specifies the characters that will successfully match a single character from a given input string.

Simple Classes

The most basic form of a character class is to simply place a set of characters side-by-side within square brackets. For example, the regular expression [bcr]at will match the words "bat", "cat", or "rat" because it defines a character class (accepting either "b", "c", or "r") as its first character.

The following examples use the test harness, RegexTestHarness, to show that this is true.

 
Current REGEX is: [rcb]at
Current INPUT is: bat
I found the text "bat" starting at index 0 and ending at index 3.

Current REGEX is: [rcb]at
Current INPUT is: cat
I found the text "cat" starting at index 0 and ending at index 3.

Current REGEX is: [rcb]at
Current INPUT is: rat
I found the text "rat" starting at index 0 and ending at index 3.

Current REGEX is: [rcb]at
Current INPUT is: hat
No match found.
In the above examples, the overall match succeeds only when the first letter matches one of the characters defined by the character class.

 

 

Negation

To match all characters except those listed, insert the ^ metacharacter at the beginning of the character class. This technique is known as negation.

 
Current REGEX is: [^bcr]at
Current INPUT is: bat
No match found.

Current REGEX is: [^bcr]at
Current INPUT is: cat
No match found.

Current REGEX is: [^bcr]at
Current INPUT is: rat
No match found.

Current REGEX is: [^bcr]at
Current INPUT is: hat
I found the text "hat" starting at index 0 and ending at index 3.
The match is successful only if the first character of the input string does not contain any of the characters defined by the character class.

 

 

Ranges

Sometimes you'll want to define a character class that includes a range of values, such as the letters "a through h" or the numbers "1 through 5". To specify a range, simply insert the - metacharacter between the first and last character to be matched, such as [1-5] or [a-h]. You can also place different ranges beside each other within the class to further expand the match possibilities. For example, [a-zA-Z] will match any letter of the alphabet: a to z (lowercase) or A to Z (uppercase).

Here are some examples of ranges and negation:

Current REGEX is: [a-c]
Current INPUT is: a
I found the text "a" starting at index 0 and ending at index 1.

Current REGEX is: [a-c]
Current INPUT is: b
I found the text "b" starting at index 0 and ending at index 1.

Current REGEX is: [a-c]
Current INPUT is: c
I found the text "c" starting at index 0 and ending at index 1.

Current REGEX is: [a-c]
Current INPUT is: d
No match found.

Current REGEX is: foo[1-5]
Current INPUT is: foo1
I found the text "foo1" starting at index 0 and ending at index 4.

Current REGEX is: foo[1-5]
Current INPUT is: foo5
I found the text "foo5" starting at index 0 and ending at index 4.

Current REGEX is: foo[1-5]
Current INPUT is: foo6
No match found.

Current REGEX is: foo[^1-5]
Current INPUT is: foo1
No match found.

Current REGEX is: foo[^1-5]
Current INPUT is: foo6
I found the text "foo6" starting at index 0 and ending at index 4.

 

 

Unions

You can also use unions to create a single character class comprised of two or more separate character classes. To create a union, simply nest one class inside the other, such as [0-4[6-8]]. This particular union creates a single character class that matches the numbers 0, 1, 2, 3, 4, 6, 7, and 8.

Current REGEX is: [0-4[6-8]]
Current INPUT is: 0
I found the text "0" starting at index 0 and ending at index 1.

Current REGEX is: [0-4[6-8]]
Current INPUT is: 5
No match found.

Current REGEX is: [0-4[6-8]]
Current INPUT is: 6
I found the text "6" starting at index 0 and ending at index 1.

Current REGEX is: [0-4[6-8]]
Current INPUT is: 8
I found the text "8" starting at index 0 and ending at index 1.

Current REGEX is: [0-4[6-8]]
Current INPUT is: 9
No match found.

 

 

Intersections

To create a single character class matching only the characters common to all of its nested classes, use the intersection operator &&, as in [0-9&&[345]]. This particular intersection creates a single character class matching only the numbers common to both character classes: 3, 4, and 5.

 
Current REGEX is: [0-9&&[345]]
Current INPUT is: 3
I found the text "3" starting at index 0 and ending at index 1.

Current REGEX is: [0-9&&[345]]
Current INPUT is: 4
I found the text "4" starting at index 0 and ending at index 1.

Current REGEX is: [0-9&&[345]]
Current INPUT is: 5
I found the text "5" starting at index 0 and ending at index 1.

Current REGEX is: [0-9&&[345]]
Current INPUT is: 2
No match found.

Current REGEX is: [0-9&&[345]]
Current INPUT is: 6
No match found.
And here's an example that shows the intersection of two ranges:

 
Current REGEX is: [2-8&&[4-6]]
Current INPUT is: 3
No match found.

Current REGEX is: [2-8&&[4-6]]
Current INPUT is: 4
I found the text "4" starting at index 0 and ending at index 1.

Current REGEX is: [2-8&&[4-6]]
Current INPUT is: 5
I found the text "5" starting at index 0 and ending at index 1.

Current REGEX is: [2-8&&[4-6]]
Current INPUT is: 6
I found the text "6" starting at index 0 and ending at index 1.

Current REGEX is: [2-8&&[4-6]]
Current INPUT is: 7
No match found.

 

 

Subtraction

Finally, you can use subtraction to negate one or more nested character classes, such as [0-9&&[^345]]. This example creates a single character class that matches everything from 0 to 9, except the numbers 3, 4, and 5.

 
Current REGEX is: [0-9&&[^345]]
Current INPUT is: 2
I found the text "2" starting at index 0 and ending at index 1.

Current REGEX is: [0-9&&[^345]]
Current INPUT is: 3
No match found.

Current REGEX is: [0-9&&[^345]]
Current INPUT is: 4
No match found.

Current REGEX is: [0-9&&[^345]]
Current INPUT is: 5
No match found.

Current REGEX is: [0-9&&[^345]]
Current INPUT is: 6
I found the text "6" starting at index 0 and ending at index 1.

Current REGEX is: [0-9&&[^345]]
Current INPUT is: 9
I found the text "9" starting at index 0 and ending at index 1.
Now that we've covered how character classes are created, You may want to review the Character Classes table before continuing with the next section.

 


 

Predefined Character Classes

The Pattern API contains a number of useful predefined character classes, which offer convenient shorthands for commonly-used regular expressions:
Predefined Character Classes
. Any character (may or may not match line terminators)
\d A digit: [0-9]
\D A non-digit: [^0-9]
\s A whitespace character: [ \t\n\x0B\f\r]
\S A non-whitespace character: [^\s]
\w A word character: [a-zA-Z_0-9]
\W A non-word character: [^\w]
In the table above, each construct in the left-hand column is shorthand for the character class in the right-hand column. For example, \d means a range of digits (0-9), and \w means a word character (any lowercase letter, any uppercase letter, the underscore character, or any digit). Use the predefined classes whenever possible. They make your code easier to read and eliminate errors introduced by malformed character classes.

Constructs beginning with a backslash are called escaped constructs; we previewed escaped constructs in the String Literals section where we mentioned the use of backslash and \Q and \E for quotation. If you are using an escaped construct within a string literal, you must preceed the backslash with another backslash for the string to compile. For example:

 
In this example \d is the regular expression; the extra backslash is required so that the code compile. Our test harness reads the expressions directly from a file, however, so the extra backslash is unnecessary.

The following examples demonstrate the use of predefined character classes. For each case, try to predicit the result before you read the output on the last line.

 
Current REGEX is: .
Current INPUT is: @
I found the text "@" starting at index 0 and ending at index 1.

Current REGEX is: .
Current INPUT is: 1
I found the text "1" starting at index 0 and ending at index 1.

Current REGEX is: .
Current INPUT is: a
I found the text "a" starting at index 0 and ending at index 1.

Current REGEX is: \d
Current INPUT is: 1
I found the text "1" starting at index 0 and ending at index 1.

Current REGEX is: \d
Current INPUT is: a
No match found.

Current REGEX is: \D
Current INPUT is: 1
No match found.

Current REGEX is: \D
Current INPUT is: a
I found the text "a" starting at index 0 and ending at index 1.

Current REGEX is: \s
Current INPUT is:  
I found the text " " starting at index 0 and ending at index 1.

Current REGEX is: \s
Current INPUT is: a
No match found.

Current REGEX is: \S
Current INPUT is:  
No match found.

Current REGEX is: \S
Current INPUT is: a
I found the text "a" starting at index 0 and ending at index 1.

Current REGEX is: \w
Current INPUT is: a
I found the text "a" starting at index 0 and ending at index 1.

Current REGEX is: \w
Current INPUT is: !
No match found.

Current REGEX is: \W
Current INPUT is: a
No match found.

Current REGEX is: \W
Current INPUT is: !
I found the text "!" starting at index 0 and ending at index 1.
In the first three examples, our regular expression is simply . (the "period" or "dot" metacharacter) which indicates "any character." Therefore, the match is successful in all three cases (a randomly-selected @ character, a digit, and a letter). The remaining examples each use a single regular expression construct from the Predefined Character Classes table. You can refer to this table to figure out the logic behind each match:

  • \d matches all digits
  • \s matches spaces
  • \w matches word characters
Alternatively, a capital letter means the opposite:

  • \D matches non-digits
  • \S matches non-spaces
  • \W matches non-word characters

 


 

Quantifiers

Quantifiers allow you to specify the number of occurances to match against. For convenience, the three sections of the API specification describing greedy, relucant, and possessive quantifiers are presented below. At first glance it may appear that the quantifiers X?, X?? and X?+ do exactly the same thing, since they all promise to match "X, once or not at all". There are subtle implementation differences which will be explained near the end of this section.

Quantifiers Meaning
Greedy Reluctant Possessive
X? X?? X?+ X, once or not at all
X* X*? X*+ X, zero or more times
X+ X+? X++ X, one or more times
X{n} X{n}? X{n}+ X, exactly n times
X{n,} X{n,}? X{n,}+ X, at least n times
X{n,m} X{n,m}? X{n,m}+ X, at least n but not more than m times

Let's start our look at greedy quantifiers by creating three different regular expressions: the letter "a" followed by either ?, *, or +. Let's modify the regex.txt input file for the test harness, RegexTestHarness and see what happens when these expressions are tested against an empty input string "":

 
Current REGEX is: a?  // looking for the letter "a", once or not at all 
Current INPUT is: 
I found the text "" starting at index 0 and ending at index 0.

Current REGEX is: a*  // looking for the letter "a", zero or more times
Current INPUT is: 
I found the text "" starting at index 0 and ending at index 0.

Current REGEX is: a+  // looking for the letter "a", one or more times
Current INPUT is: 
No match found.

Zero-Length Matches

In the above example, the match is successful in the first two cases, because the expressions a? and a* both allow for zero occurances of the letter a. You'll also notice that the start and end indices are both zero, which is unlike any of the examples we've seen so far. The empty input string "" has no length, so the test simply matches nothing at index 0. Matches of this sort are known as a zero-length matches. A zero-length match can occur in a several cases: in an empty input string, at the beginning of an input string, after the last character of an input string, or in between any two characters of an input string. Zero-length matches are easily identifiable because they always start and end at the same index position.

Let's explore zero-length matches with a few more examples. Change the input string to a single letter "a" and you'll notice something interesting:

 
Current REGEX is: a?
Current INPUT is: a
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.

Current REGEX is: a*
Current INPUT is: a
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.

Current REGEX is: a+
Current INPUT is: a
I found the text "a" starting at index 0 and ending at index 1.
All three quantifiers found the letter "a", but the first two also found a zero-length match at index 1; that is, after the last character of the input string. Remember, the matcher sees the character "a" as sitting in the cell between index 0 and index 1, and our test harness loops until it can no longer find a match. Depending on the quantifier used, the presence of "nothing" at the index after the last character may or may not trigger a match.

Now change the input string to the letter "a" five times in a row and you'll get the following:

 
Current REGEX is: a?
Current INPUT is: aaaaa
I found the text "a" starting at index 0 and ending at index 1.
I found the text "a" starting at index 1 and ending at index 2.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "a" starting at index 3 and ending at index 4.
I found the text "a" starting at index 4 and ending at index 5.
I found the text "" starting at index 5 and ending at index 5.

Current REGEX is: a*
Current INPUT is: aaaaa
I found the text "aaaaa" starting at index 0 and ending at index 5.
I found the text "" starting at index 5 and ending at index 5.

Current REGEX is: a+
Current INPUT is: aaaaa
I found the text "aaaaa" starting at index 0 and ending at index 5.
The expression a? finds an individual match for each character, since it matches when "a" appears zero or one times. The expression a* finds two separate matches: all of the letter a's in the first match, then the zero-length match after the last character at index 5. And finally, a+ matches all occurances of the letter a, ignoring the presense of "nothing" at the last index.

At this point, you might be wondering what the results would be if the first two quantifiers encounter a letter other than "a". For example, what happens if it encounters the letter "b", as in "ababaaaab"?

Let's find out:

 
Current REGEX is: a?
Current INPUT is: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "" starting at index 3 and ending at index 3.
I found the text "a" starting at index 4 and ending at index 5.
I found the text "a" starting at index 5 and ending at index 6.
I found the text "a" starting at index 6 and ending at index 7.
I found the text "a" starting at index 7 and ending at index 8.
I found the text "" starting at index 8 and ending at index 8.
I found the text "" starting at index 9 and ending at index 9.

Current REGEX is: a*
Current INPUT is: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "" starting at index 3 and ending at index 3.
I found the text "aaaa" starting at index 4 and ending at index 8.
I found the text "" starting at index 8 and ending at index 8.
I found the text "" starting at index 9 and ending at index 9.

Current REGEX is: a+
Current INPUT is: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "aaaa" starting at index 4 and ending at index 8.
Even though the letter "b" appears in cells 1, 3, and 8, the output reports a zero-length match at those locations. The regular expression a? is not specifically looking for the letter "b"; it's merely looking for the presence (or lack thereof) of the letter "a". If the quantifier allows for a match of "a" zero times, anything in the input string that's not an "a" will show up as a zero-length match. The remaining a's are matched according to the rules discussed in the previous examples.

To match a pattern exactly n number of times, simply specify the number inside a set of braces:

 
Current REGEX is: a{3}
Current INPUT is: aa
No match found.

Current REGEX is: a{3}
Current INPUT is: aaa
I found the text "aaa" starting at index 0 and ending at index 3.

Current REGEX is: a{3}
Current INPUT is: aaaa
I found the text "aaa" starting at index 0 and ending at index 3.
Here, the regular expression a{3} is searching for three occurences of the letter "a" in a row. The first test fails because the input string does not have enough a's to match against. The third test contains exactly 3 a's in the input string, which triggers a match. The fourth example also triggers a match because there are exactly 3 a's at the beginning of the input string. Anything following that is irrelevant to the first match. If the pattern should appear again after that point, it would trigger subsequent matches:

 
Current REGEX is: a{3}
Current INPUT is: aaaaaaaaa
I found the text "aaa" starting at index 0 and ending at index 3.
I found the text "aaa" starting at index 3 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
To require a pattern to appear at least n times, add a comma after the number:

 
Current REGEX is: a{3,}
Current INPUT is: aaaaaaaaa
I found the text "aaaaaaaaa" starting at index 0 and ending at index 9.
With the same input string, this test finds only one match, because the 9 a's in a row satisfy the need for "at least" 3 a's.

Finally, to specify an upper limit on the number of occurances, add a second number inside the braces:

 
Current REGEX is: a{3,6} // find at least 3 (but no more than 6) a's in a row
Current INPUT is: aaaaaaaaa
I found the text "aaaaaa" starting at index 0 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
Here the first match is forced to stop at the upper limit of 6 characters. The second match includes whatever is left over, which happens to be three a's--the mimimum number of characters allowed for this match. If the input string were one character shorter, there would not be a second match since only two a's would remain.

Capturing Groups and Character Classes with Quantifiers

Until now, we've only tested quantifiers on input strings containing one character. In fact, quantifiers can only attach to one character at a time, so the regular expression "abc+" would mean "a, followed by b, followed by c one or more times". It would not mean "abc" one or more times. However, quantifiers can also attach to Character Classes and Capturing Groups, such as [abc]+ (a or b or c, one or more times) or (abc)+ (the group "abc", one or more times).

Let's illustrate by specifing the group (dog), three times in a row.

 
Current REGEX is: (dog){3}
Current INPUT is: dogdogdogdogdogdog
I found the text "dogdogdog" starting at index 0 and ending at index 9.
I found the text "dogdogdog" starting at index 9 and ending at index 18.

Current REGEX is: dog{3}
Current INPUT is: dogdogdogdogdogdog
No match found.
Here the first example finds three matches, since the quantifier applies to the entire capturing group. Remove the parenthesis, however, and the match fails because the quantifier {3} now applies only to the letter "g".

Similarly, we can apply a quantifier to an entire character class:

Current REGEX is: [abc]{3}
Current INPUT is: abccabaaaccbbbc
I found the text "abc" starting at index 0 and ending at index 3.
I found the text "cab" starting at index 3 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
I found the text "ccb" starting at index 9 and ending at index 12.
I found the text "bbc" starting at index 12 and ending at index 15.

Current REGEX is: abc{3}
Current INPUT is: abccabaaaccbbbc
No match found.
Here the quantifier {3} applies to the entire character class in the first example, but only to the letter "c" in the second.

Differences Among Greedy, Reluctant, and Possessive Quantifiers

As mentioned earlier, there are subtle differences among greedy, reluctant, and possessive quantifiers.

Greedy quantifiers are considered "greedy" because they force the matcher to read in, or eat, the entire input string prior to attempting the first match. If the first match attempt (the entire input string) fails, the matcher backs off the input string by one character and tries again, repeating the process until a match is found or there are no more characters left to back off from. Depending on the quantifier used in the expression, the last thing it will try matching against is 1 or 0 characters.

The reluctant quantifiers, however, take the opposite approach: they start at the beginning of the input string, then reluctantly eat one character at a time looking for a match. The last thing they try is the entire input string.

Finally, the possessive quantifiers always eat the entire input string, trying once (and only once) for a match. Unlike the greedy quantifiers, possessive quantifiers never back off, even if doing so would allow the overall match to succeed.

To illustrate, consider the input string xfooxxxxxxfoo.

 
Current REGEX is: .*foo  // greedy quantifier
Current INPUT is: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.

Current REGEX is: .*?foo  // reluctant quantifier
Current INPUT is: xfooxxxxxxfoo
I found the text "xfoo" starting at index 0 and ending at index 4.
I found the text "xxxxxxfoo" starting at index 4 and ending at index 13.

Current REGEX is: .*+foo // possessive quantifier
Current INPUT is: xfooxxxxxxfoo
No match found.
The first example uses the greedy quantifier .* to find "anything", zero or more times, followed by the letters "f" "o" "o". Because the quantifier is greedy, the .* portion of the expression first eats the entire input string. At this point, the overall expression cannot succeed, because the last three letters ("f" "o" "o") have already been consumed. So the matcher slowly backs off one letter at a time until the rightmost occurrence of "foo" has been regurgitated, at which point the match succeeds and the search ends.

The second example, however, is reluctant, so it starts by first consuming "nothing". Because "foo" doesn't appear at the beginning of the string, it's forced to swallow the first letter (an "x"), which triggers the first match at 0 and 4. Our test harness continues the process until the input string is exhausted. It finds another match at 4 and 13.

The third example fails to find a match because the quantifier is possessive. In this case, the entire input string is consumed by .*+, leaving nothing left over to satisfy the "foo" at the end of the expression. Use a possessive quantifier for situations where you want to sieze all of something without ever backing off; it will outperform the equivalent greedy quantifier in cases where the match is not immediately found.

 


 

Capturing Groups

In the previous section, we saw how quantifiers attach to one character, character class, or capturing group at a time. But until now, we have not discussed the notion of capturing groups in any detail.

Capturing groups are a way to treat multiple characters as a single unit. They are created by placing the characters to be grouped inside a set of parenthesis. For example, the regular expression (dog) creates a single group containing the letters "d" "o" and "g". The portion of the input string that matches the capturing group will be saved in memory for later recall via backreferences.

Numbering

As described in the Pattern API, capturing groups are numbered by counting their opening parentheses from left to right. In the expression ((A)(B(C))), for example, there are four such groups:
  1. ((A)(B(C)))
  2. (A)
  3. (B(C))
  4. (C)

To find out how many groups are present in the expression, call the groupCount method on a matcher object. The groupCount method returns an int showing the number of capturing groups present in the matcher's pattern. In this example, groupCount would return the number 4, showing that the pattern contains 4 capturing groups.

There is also a special group, group 0, which always represents the entire expression. This group is not included in the total reported by groupCount. Also note that groups beginning with (? are pure, non-capturing groups that do not capture text and do not count towards the group total. (You'll see examples of non-capturing groups later in the section Methods of the Pattern Class)

It's important to understand how groups are numbered because some Matcher methods accept an int specifying a particular group number as a parameter:

  • public int start(int group): Returns the start index of the subsequence captured by the given group during the previous match operation.

  • public int end (int group): Returns the index of the last character, plus one, of the subsequence captured by the given group during the previous match operation.

  • public int group (int group): Returns the input subsequence captured by the given group during the previous match operation.

Backreferences

The section of the input string matching the capturing group(s) is saved in memory for later recall via a backreference. A backreference is specified in the regular expression as a backslash (\) followed by a digit indicating the number of the group to be recalled. For example, the expression (\d\d) defines one capturing group matching two digits in a row, which can be recalled later in the expression via the backreference \1.

To match any 2 digits, followed by the exact same two digits, you would use (\d\d)\1 as the regular expression:

 
Current REGEX is: (\d\d)\1
Current INPUT is: 1212
I found the text "1212" starting at index 0 and ending at index 4.
If you change the last two digits and the match will fail:

 
Current REGEX is: (\d\d)\1
Current INPUT is: 1234
No match found.
For nested capturing groups, backreferencing works in exactly the same way: Specify a backslash followed by the number of the group to be recalled.

 


 

Boundary Matchers

Until now, we've only been interested in whether or not a match is found at some location within a particular input string. We never cared about where in the string the match was taking place.

You can make your pattern matches more precise by specifying such information with boundary matchers. For example, maybe you're interested in finding a particular word, but only if it appears at the beginning or end of a line. Or maybe you want to know if the match is taking place on a word boundary, or at the end of the previous match.

The following table lists and explains all the boundary matchers.

Boundary Matchers
^ The beginning of a line
$ The end of a line
\b A word boundary
\B A non-word boundary
\A The beginning of the input
\G The end of the previous match
\Z
\z The end of the input
The following examples demonstrate the use of boundary matchers ^ and $. As noted above, ^ matches the beginning of a line, and $ matches the end.

 
Current REGEX is: ^dog$
Current INPUT is: dog
I found the text "dog" starting at index 0 and ending at index 3.

Current REGEX is: ^dog$
Current INPUT is:       dog
No match found.

Current REGEX is: \s*dog$
Current INPUT is:             dog
I found the text "            dog" starting at index 0 and ending at index 15.

Current REGEX is: ^dog\w*
Current INPUT is: dogblahblah
I found the text "dogblahblah" starting at index 0 and ending at index 11.
The first example is successful because the pattern occupies the entire input string. The second example fails because the input string contains extra whitespace at the beginning. The third example specifies an expression that allows for unlimited white space, followed by "dog" on the end of the line. The fourth example requires "dog" to be present at the beginning of a line followed by an unlimited number of word characters.

To check if a pattern begins and ends on a word boundary (as opposed to a substring within a longer string), just use \b on either side; for example, \bdog\b

 
Current REGEX is: \bdog\b
Current INPUT is: The dog plays in the yard.
I found the text "dog" starting at index 4 and ending at index 7.

Current REGEX is: \bdog\b
Current INPUT is: The doggie plays in the yard.
No match found.
To match the expression on a non-word boundary, use \B instead:

 
Current REGEX is: \bdog\B
Current INPUT is: The dog plays in the yard.
No match found.

Current REGEX is: \bdog\B
Current INPUT is: The doggie plays in the yard.
I found the text "dog" starting at index 4 and ending at index 7.
To require the match to occur only at the end of the previous match, use \G:

 
Current REGEX is: dog // Without \G
Current INPUT is: dog dog
I found the text "dog" starting at index 0 and ending at index 3.
I found the text "dog" starting at index 4 and ending at index 7.

Current REGEX is: \Gdog // With \G
Current INPUT is: dog dog
I found the text "dog" starting at index 0 and ending at index 3.
Here the second example finds only one match, because the second occurrence of "dog" does not start at the end of the previous match.

 


 

Methods of the Pattern Class

Until now, we've only used the test harness to create Pattern objects in their most basic form. This section explores advanced techniques such as creating patterns with flags and using embedded flag expressions. It also explores some remaining methods we haven't discussed yet.

Creating a Pattern with Flags

The Pattern class defines an alternate compile method that accepts a set of flags affecting the way the pattern is matched. The flags parameter is a bit mask that may include any of the following public static fields:

In the following steps we will modify the test harness, RegexTestHarness to create a pattern with case-insensitive matching.

First, modify the code to call the alternate version of compile:

pattern = Pattern.compile(REGEX,Pattern.CASE_INSENSITIVE);
Then edit your input file, regex.txt, to contain the following:

 
dog
DoGDOg
Finally, compile and run the test harness to get the following results:

 
Current REGEX is: dog
Current INPUT is: DoGDOg
I found the text "DoG" starting at index 0 and ending at index 3.
I found the text "DOg" starting at index 3 and ending at index 6.
As you can see, the string literal "dog" matches both occurances, regardless of case. To compile a pattern with multiple flags, separate the flags to be included using the bitwise OR operator (|):

 
pattern = Pattern.compile("[az]$", Pattern.MULTILINE | Pattern.UNIX_LINES);
Note that for clarity you could also specify an int variable:

 
Pattern pattern = Pattern.compile("aa", flags);

Embedded Flag Expressions

It's also possible to enable various flags using embedded flag expressions. Embedded flag expressions are an alternative to the two-argument version of compile, and are specified in the regular expression itself. The following example uses the original test harness, RegexTestHarness.java with the embedded flag expression (?i) to enable case-insensitive matching.

 
Current REGEX is: (?i)foo
Current INPUT is: FOOfooFoOfoO
I found the text "FOO" starting at index 0 and ending at index 3.
I found the text "foo" starting at index 3 and ending at index 6.
I found the text "FoO" starting at index 6 and ending at index 9.
I found the text "foO" starting at index 9 and ending at index 12.
Once again, all matches succeed regardless of case.

The embedded flag expressions that correspond to Pattern's publicly-accessible fields are presented in the following table:

 

Constant Equivalent Embedded Flag Expression
Pattern.CANON_EQ None
Pattern.CASE_INSENSITIVE (?i)
Pattern.COMMENTS (?x)
Pattern.MULTILINE (?m)
Pattern.DOATALL (?s)
Pattern.UNICODE_CASE (?u)
Pattern.UNIX_LINES (?d)

Using the matches(String,CharSequence) Method

The Pattern class defines a convenient Pattern.html#matches(java.lang.String, java.lang.CharSequence)">matches method that allows you to quickly check if a pattern is present in a given input string. As with all public static methods, you should call matches with its class name, such as Pattern.matches("\\d","1"); In this example, the method returns true, because the digit "1" matches the regular expression \d.

Using the split(String) method

The java.lang.CharSequence)">split method is a great tool for gathering the text that lies on either side of the pattern that's been matched. As shown below in the SplitTest code, the split method could extract the words "one two three four five" from the string "one:two:three:four:five":

 
import java.util.regex.*;

private static String REGEX = ":";
private static String INPUT = "one:two:three:four:five";
    
public static void main(String[] argv) 
{
    Pattern p = Pattern.compile(REGEX);
    String[] items = p.split(INPUT);

    for(int i=0;i<items.length;i++) 
    {
        System.out.println(items[i]);
        }
    }
}

OUTPUT:

one
two
three
four
five
For simplicity, we've matched a string literal, the colon (:) instead of a complex regular expression. Since we're still using Pattern and Matcher objects, you can use split to get the text that falls on either side of any regular expression. Here's the same example, SplitTest2, modified to split on digits instead:

 
import java.util.regex.*;


    private static String REGEX = "\\d";
    private static String INPUT = "one9two4three7four1five";

    public static void main(String[] argv) {
        Pattern p = Pattern.compile(REGEX);
        String[] items = p.split(INPUT);
        for(int i=0;i<items.length;i++) {
            System.out.println(items[i]);
        }
    }
}

OUTPUT:

one
two
three
four
five

 

Pattern Method Equivalents in java.lang.String

Regular expression support has also been introduced to java.lang.String through several methods that mimic the behavior of java.util.regex.Pattern. For convenience, key excerpts from their API are presented below.

  • java.lang.String)">public boolean matches(String regex) java.lang.String)">>: Tells whether or not this string matches the given regular expression. An invocation of this method of the form str.matches(regex) yields exactly the same result as the expression Pattern.matches(regex, str).

  • int)">public String[] split(String regex, int limit): Splits this string around matches of the given regular expression. An invocation of this method of the form str.split(regex, n) yields the same result as the expression Pattern.compile(regex).split(str, n)

  • java.lang.String)">public String[] split(String regex): Splits this string around matches of the given regular expression. This method works the same as if you invoked the two-argument split method with the given expression and a limit argument of zero. Trailing empty strings are not included in the resulting array.

 

 

Index Methods

Index methods provide useful index values that show precisely where the match was found in the input string:

 

 

Study Methods

Study methods review the input string and return a boolean indicating whether or not the pattern is found.

 

 

Replacement Methods

Replacement methods are useful methods for replacing text in an input string.
  1. public Matcher appendReplacement(StringBuffer sb, String replacement)

  2. java.lang.StringBuffer)"> public StringBuffer appendTail(StringBuffer sb)
  3. java.lang.String)">public String replaceAll(String replacement)
  4. java.lang.String)">public String replaceFirst(String replacement)

Using the start and end Methods

Here's an example, MatcherTest, that counts the number of times the word "dog" appears in the input string.

import java.util.regex.*;

public static void main(String[] argv) 
{
   Pattern p = Pattern.compile(REGEX);
   Matcher m = p.matcher(INPUT); // get a matcher object
   int count = 0;
   while(m.find()) 
   {
       count++;
       System.out.println("Match number "+count);
       System.out.println("start(): "+m.start());
       System.out.println("end(): "+m.end());
   }
}



OUTPUT:

Match number 1
start(): 0
end(): 3
Match number 2
start(): 4
end(): 7
Match number 3
start(): 8
end(): 11
You can see that this example uses word boundaries to ensure that the letters "d" "o" "g" are not merely a substring in a longer word. It also gives some useful information about where in the input string the match has occurred. The start method returns the start index of the subsequence captured by the given group during the previous match operation, and end returns the index of the last character matched, plus one.

Using the matches and lookingAt Methods

The matches and lookingAt methods both attempt to match an input sequence against a pattern. The difference, however, is that matches requires the entire input sequence to be matched, while lookingAt does not. Both methods always start at the beginning of the input string. Here's the full code, MatchesLooking:

import java.util.regex.*;


    private static Pattern pattern;
    private static Matcher matcher;

    public static void main(String[] argv) {
   
      // Initialize
        pattern = Pattern.compile(REGEX);
        matcher = pattern.matcher(INPUT);

        System.out.println("Current REGEX is: "+REGEX);
        System.out.println("Current INPUT is: "+INPUT);

        System.out.println("lookingAt(): "+matcher.lookingAt());
        System.out.println("matches(): "+matcher.matches());

    }
}

Current REGEX is: foo
Current INPUT is: fooooooooooooooooo
lookingAt(): true
matches(): false

Using replaceFirst(String) and replaceAll(String)

The replaceFirst and replaceAll methods replace text that matches a given regular expression. As their names indicate, replaceFirst replaces the first occurrence, and replaceAll replaces all occurances. Here's the ReplaceTest:

 
    private static String REGEX = "dog";
    private static String INPUT = "The dog says meow. All dogs say meow.";
    private static String REPLACE = "cat";
 
    public static void main(String[] argv) {
        Pattern p = Pattern.compile(REGEX);
        Matcher m = p.matcher(INPUT); // get a matcher object
        INPUT = m.replaceAll(REPLACE);
        System.out.println(INPUT);
    }
}

OUTPUT: The cat says meow. All cats say meow.
In this first version, all occurrances of "dog" are replaced with "cat". But why stop here? Rather than replace a simple literal like "dog", you can replace text that matches any regular expression. The API for this method states that "given the regular expression a*b, the input 'aabfooaabfooabfoob', and the replacement string '-', an invocation of this method on a matcher for that expression would yield the string '-foo-foo-foo-'."

Here's the code, ReplaceTest2:

import java.util.regex.*;
 
 
    private static String REGEX = "a*b";
    private static String INPUT = "aabfooaabfooabfoob";
    private static String REPLACE = "-";
 
    public static void main(String[] argv) {
        Pattern p = Pattern.compile(REGEX);
        Matcher m = p.matcher(INPUT); // get a matcher object
        INPUT = m.replaceAll(REPLACE);
        System.out.println(INPUT);
    }
}

OUTPUT: -foo-foo-foo-                                                                      
To replace only the first occurrance of the pattern, simply call replaceFirst instead of replaceAll. It accepts the same parameter.

The appendReplacement(StringBuffer,String) and appendTail(StringBuffer)

The Matcher class also provides appendReplacement and appendTail methods for text replacement. Here's an example that uses these two methods to achieve the same effect as replaceAll.

import java.util.regex.*;
 
 
    private static String REGEX = "a*b";
    private static String INPUT = "aabfooaabfooabfoob";
    private static String REPLACE = "-";
 
    public static void main(String[] argv) {
        Pattern p = Pattern.compile(REGEX);
        Matcher m = p.matcher(INPUT); // get a matcher object
        StringBuffer sb = new StringBuffer();
        while(m.find()){
            m.appendReplacement(sb,REPLACE);
        }
        m.appendTail(sb);
        System.out.println(sb.toString());
    }
}

OUTPUT: -foo-foo-foo- 

Matcher Method Equivalents in java.lang.String

For convenience, the String class mimics a couple of Matcher methods as well:

  • public String replaceFirst(String regex, String replacement): Replaces the first substring of this string that matches the given regular expression with the given replacement. An invocation of this method of the form str.replaceFirst(regex, repl) yields exactly the same result as the expression Pattern.compile(regex).matcher(str).replaceFirst(repl)

  • public String replaceAll(String regex, String replacement): Replaces each substring of this string that matches the given regular expression with the given replacement. An invocation of this method of the form str.replaceAll(regex, repl) yields exactly the same result as the expression Pattern.compile(regex).matcher(str).replaceAll(repl)

 


 

PatternSyntaxException

A PatternSyntaxException is an unchecked exception that indicates a syntax error in a regular expression pattern. The PatternSyntaxException class provides the following methods to help you determine what went wrong:

The following source code, RegexTestHarness2 , updates our test harness to check for malformed regular expressions. It intentionally introduces a syntax error, then examines the caught exception using the methods listed above. Changes are marked in bold.

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

private static String REGEX;
private static String INPUT;
private static BufferedReader br;
private static Pattern pattern;
private static Matcher matcher;
private static boolean found;

public static void main(String[] argv) 
{
    initResources();
    processTest();
    closeResources();
}

private static void initResources() 
{
   try 
   {
       br = new BufferedReader(new FileReader("regex.txt"));
   }
   catch  FileNotFoundException(fnfe) 
   {
        System.out.println("Cannot locate input file! "+fnfe.getMessage());
        System.exit(0);
    }
   try 
   {
   REGEX = br.readLine();
       INPUT = br.readLine();
   } 
   catch  IOException(ioe) 
   {
   }

   try 
    {
    pattern = Pattern.compile(REGEX);
    matcher = pattern.matcher(INPUT);
   } 
   catch(PatternSyntaxException pse) 
   {
       System.out.println("There is a problem with the regular expression!");
       System.out.println("The pattern in question is: "+pse.getPattern());
       System.out.println("The description is: "+pse.getDescription());
       System.out.println("The message is: "+pse.getMessage());
       System.out.println("The index is: "+pse.getIndex());
       System.exit(0);
    }

    System.out.println("Current REGEX is: "+REGEX);
    System.out.println("Current INPUT is: "+INPUT);
}

private static void processTest() 
{
    while(matcher.find()) 
    {
        System.out.println("I found the text \"" + matcher.group() +
                           "\" starting at index " + matcher.start() +
                           " and ending at index " + matcher.end() + ".");
        found = true;
    }

    if(!found)
    {
        System.out.println("No match found.");
    }
}

private static void closeResources() 
{
   try
   {
       br.close();
   }
   catch IOException(ioe)
   {
   }
}
To run this test, change the file regex.txt to contain ?i)foo as its first line, and FoO as its second line. This mistake is a common scenario in which the programmer has forgetten the opening parenthesis in the embedded flag expression (?i).

OUTPUT:

There is a problem with the regular expression!
The pattern in question is: ?i)foo
The description is: Dangling meta character '?'
The message is: Dangling meta character '?' near index 0 
?i)foo
^
The index is: 0
From this output, we can see that the syntax error is a dangling metacharacter (the question mark) at index 0. A missing opening parenthesis is the culprit.

  Home