Regex End of Line and Start of Line in Java (^, $)

Regex end of line and start of line in Java with $ and ^, MULTILINE mode, the trailing line break trap, \A and \z, and Windows line breaks with \R.

The input c, a, t, line feed with five positions. The caret and backslash-A match at position 0. The dollar sign and backslash-Z match at position 3 before the line feed and at position 4 at the end. Backslash-z matches only at position 4.

To match the end of a line in a regex, we use the dollar sign $, and to match the start of a line, we use the caret ^. Both are anchors, so they match a position in the text, not a character. By default they match only at the start and the end of the whole string. With the MULTILINE flag, or (?m) inside the pattern, they also match at the start and end of every line.

We use anchors to check that a value starts or ends with a pattern, to validate a whole value with find(), and to process a multi-line text such as a config file or a log line by line.

The following example shows the most common anchor checks in Java, with the result of every check as a trailing comment.

boolean startsThe = Pattern.compile("^The").matcher("The sun is shining").find();    // true
boolean endsIng = Pattern.compile("ing$").matcher("The sun is shining").find();      // true
boolean startsSun = Pattern.compile("^sun").matcher("The sun is shining").find();    // false

String fruits = "apple\nbanana\ncherry";
boolean wholeText = Pattern.compile("^banana$").matcher(fruits).find();                     // false
boolean oneLine = Pattern.compile("^banana$", Pattern.MULTILINE).matcher(fruits).find();    // true
List<String> firstWords = Pattern.compile("(?m)^\\w+").matcher(fruits).results().map(MatchResult::group).toList();   // [apple, banana, cherry]

boolean beforeNewline = Pattern.compile("cat$").matcher("cat\n").find();     // true
boolean absoluteEnd = Pattern.compile("cat\\z").matcher("cat\n").find();     // false
String[] lines = "a\r\nb\nc".split("\\R");                                   // [a, b, c]

Notice the last three lines. The $ anchor also matches before a final line break, while \z does not, and \R splits on every kind of line break. The quick-reference table lists each pattern, and every row has its own section after it.

To matchRegexJava string literal
“The” at the start of the string^The“^The”
“ing” at the end of the stringing$“ing$”
A whole string of letters^[a-z]+$“^[a-z]+$”
A word at the start of any line(?m)^\w+“(?m)^\\w+”
The very end of the input\z“\\z”
Any line break (\n, \r\n, \r)\R“\\R”

We also cover two details that cause real bugs, namely that $ matches before a final line break and that Windows line breaks are two characters long.

1. Anchors Have Zero Width

An anchor checks where the regex engine is, and it consumes no characters. The match of a lone ^ or $ is an empty string. Inserting text at the anchor position shows the effect, because replaceAll() puts the replacement at the position and removes no character of the input.

String prefixed = "cat".replaceAll("^", ">");      // ">cat"
String suffixed = "cat".replaceAll("$", "!");      // "cat!"

Matcher end = Pattern.compile("$").matcher("cat");
boolean found = end.find();                        // true
int position = end.start();                        // 3
String matched = end.group();                      // ""

The position 3 is the place after “t”. Because anchors have zero width, ^The matches the same three characters as The. The anchor only adds a condition, namely that nothing comes before “The”.

2. Regex for “Starts With” Using the Caret ^

A caret at the beginning of a pattern makes the match start at position 0. We use it with Matcher.find(), which searches the input, so the rest of the string can contain anything.

The string starts withRegexJava string literal
A digit^\d“^\\d”
An ASCII letter^[a-zA-Z]“^[a-zA-Z]”
The word “Hello”^Hello“^Hello”
A character that is not a letter or digit^[^a-zA-Z0-9]“^[^a-zA-Z0-9]”
“hello” in any case(?i)^hello“(?i)^hello”
boolean digit = Pattern.compile("^\\d").matcher("1st place").find();              // true
boolean letter = Pattern.compile("^[a-zA-Z]").matcher("Lokesh").find();           // true
boolean hello = Pattern.compile("^Hello").matcher("Hello world").find();          // true
boolean notFirst = Pattern.compile("^Hello").matcher("Say Hello").find();         // false
boolean symbol = Pattern.compile("^[^a-zA-Z0-9]").matcher("#tag").find();         // true
boolean anyCase = Pattern.compile("(?i)^hello").matcher("HELLO there").find();    // true

The caret has a second meaning. Right after an opening bracket, it negates a character class. In ^[^a-zA-Z0-9], the first caret is the anchor and the second one means “not”.

When the prefix is fixed text, String.startsWith() is clearer and faster than a regex. The regex version is the better choice only when the start is a pattern, such as “a digit” or “one of several words”.

boolean prefix = "Hello world".startsWith("Hello");      // true

3. Regex for “Ends With” Using the Dollar Sign $

A dollar sign at the end of a pattern makes the match end at the last position of the input. The checks mirror the “starts with” cases.

boolean digitEnd = Pattern.compile("\\d$").matcher("order 42").find();                  // true
boolean worldEnd = Pattern.compile("world$").matcher("Hello world").find();             // true
boolean worldStart = Pattern.compile("world$").matcher("world peace").find();           // false
boolean punctuation = Pattern.compile("[.!?]$").matcher("Done!").find();                // true
boolean image = Pattern.compile("\\.(jpg|png)$").matcher("photo.png").find();           // true
boolean notImage = Pattern.compile("\\.(jpg|png)$").matcher("photo.png.txt").find();    // false

The file extension check shows why the anchor matters. An upload form that accepts only images must reject “photo.png.txt”, but without the $ the pattern \.(jpg|png) also finds “.png” inside that name. For one fixed suffix, String.endsWith() does the same job, so “photo.png”.endsWith(“.png”) returns true.

4. Matching the Whole String With ^ and $

With both anchors, the pattern must cover the input from the first character to the last. We validate a value with find() this way, because without anchors find() accepts any input that contains a match somewhere.

boolean partial = Pattern.compile("[a-z]+").matcher("abc123").find();      // true
boolean whole = Pattern.compile("^[a-z]+$").matcher("abc123").find();      // false
boolean gang = Pattern.compile("^g.*g$").matcher("gang").find();          // true
boolean empty = Pattern.compile("^$").matcher("").find();                 // true

boolean noAnchors = "abc".matches("[a-z]+");                               // true

The first line is true because “abc” is found inside “abc123”. The methods String.matches() and Matcher.matches() already require the whole input to match, so ^ and $ add nothing there. With matches(), we leave out the anchors; with find(), we add them. The article regex for alphanumeric characters uses the same rule for input validation.

5. Matching Every Line With MULTILINE

When the input has several lines, ^ and $ still match only at the start and end of the whole text. The Pattern.MULTILINE flag changes that, so ^ also matches right after each line break and $ right before each line break. The inline form of the flag is (?m).

The text apple, line break, banana, line break, cherry, with caret and dollar positions marked once for the whole string in default mode and at every line in MULTILINE mode
In default mode, ^ and $ match once each; with MULTILINE, they match at the start and end of every line

A Java text block is a convenient way to write multi-line input. The closing quotes on the last line mean the text has no final line break.

String sentences = """
    The sun is shining
    Apples are delicious
    The moon is bright
    Birds are singing""";

List<String> startsThe = Pattern.compile("^The.*", Pattern.MULTILINE).matcher(sentences).results().map(MatchResult::group).toList();   // [The sun is shining, The moon is bright]
List<String> endsIng = Pattern.compile("(?m)^.*ing$").matcher(sentences).results().map(MatchResult::group).toList();                  // [The sun is shining, Birds are singing]
List<String> noFlag = Pattern.compile("^.*ing$").matcher(sentences).results().map(MatchResult::group).toList();                        // []

The last line returns an empty list. Without MULTILINE, ^ is only at “The sun”, and .* stops at the first line break, so $ is never reached. The pattern ^.*ing$ works line by line only because the dot does not match a line break. If the pattern also uses DOTALL, the dot crosses lines and the result changes, as the regex match any character article explains.

A real case is a properties file in which lines starting with “#” are comments. The pattern (?m)^#.*$ finds every comment line, and replacing it with an empty string removes them before parsing. For simple line filters, String.lines() is often easier to read than a MULTILINE regex.

String config = "# db\nurl=x\n# cache\nsize=5";
long comments = Pattern.compile("(?m)^#.*$").matcher(config).results().count();    // 2
List<String> settings = config.lines().filter(line -> !line.startsWith("#")).toList();   // [url=x, size=5]

6. The Trailing Line Break Trap

In Java, $ matches at the end of the input and also right before a line break at the very end. The rule exists so that a line read with its “\n” still matches word$. It confuses many developers, because the line break is not part of the match.

The input c, a, t, line feed with five positions. The caret and backslash-A match at position 0. The dollar sign and backslash-Z match at position 3 before the line feed and at position 4 at the end. Backslash-z matches only at position 4.
$ and \Z accept a final line break, \z matches only at the absolute end
boolean found = Pattern.compile("cat$").matcher("cat\n").find();      // true
boolean whole = "cat\n".matches("cat$");                              // false

String twice = "a\nb\n".replaceAll("$", "!");                         // "a\nb!\n!"
String everyLine = "a\nb\n".replaceAll("(?m)$", "!");                 // "a!\nb!\n!"
String quoted = "a\nb\n".replaceAll("(?m)^", "> ");                   // "> a\n> b\n"

The second line is false because nothing in the pattern matches the “\n”. The third result shows the rule at work, since without MULTILINE, $ still matches twice, once before the final “\n” and once at the very end. The last line shows a related rule. In MULTILINE mode, ^ does not match after a line break that ends the input, so no “> ” is added at the end.

The trap matters for validation. A check written as ^\d+$ with find() accepts “123\n”, a value with a hidden line break, and that value can end up in a database column or a file name. The next section shows the fix.

7. \A, \z and \Z as Anchors for the Whole Input

Java has three anchors that ignore the MULTILINE flag. They always refer to the whole input, as the boundary matchers part of the Pattern Javadoc defines.

AnchorMatches atWith a final line break “abc\n”
^Start of input; start of each line in MULTILINEBefore “a”
$End of input or before a final line break; end of each line in MULTILINEBefore and after “\n”
\AStart of input onlyBefore “a”
\ZEnd of input, or before a final line breakBefore and after “\n”
\zThe absolute end of input onlyAfter “\n” only
String fruits = "apple\nbanana\ncherry";
boolean lineStart = Pattern.compile("(?m)^banana").matcher(fruits).find();       // true
boolean inputStart = Pattern.compile("(?m)\\Abanana").matcher(fruits).find();    // false
boolean lineEnd = Pattern.compile("(?m)apple$").matcher(fruits).find();          // true
boolean inputEnd = Pattern.compile("(?m)apple\\z").matcher(fruits).find();       // false

boolean upperZ = Pattern.compile("cat\\Z").matcher("cat\n").find();             // true
boolean lowerZ = Pattern.compile("cat\\z").matcher("cat\n").find();             // false

For strict validation with find(), we use \A and \z, or we call matches() instead. Both reject a value with a trailing line break.

boolean slipsThrough = Pattern.compile("^\\d+$").matcher("123\n").find();     // true
boolean strict = Pattern.compile("\\A\\d+\\z").matcher("123\n").find();       // false
boolean matchesWhole = "123\n".matches("\\d+");                               // false

8. Windows Line Breaks and \R

Text from files and HTTP requests often uses the Windows line break \r\n, which is two characters. Java handles it correctly in MULTILINE mode, where $ matches before the \r, not between \r and \n. Java recognizes these line terminators for ^ and $.

  • The line feed \n ends lines on Unix and macOS.
  • The pair \r\n (carriage return plus line feed) ends lines on Windows.
  • A lone carriage return \r ends lines in old Mac files.
  • The Unicode characters \u0085, \u2028 and \u2029 (next line, line separator and paragraph separator) also end lines.

The UNIX_LINES flag, inline (?d), limits the list to \n only. With it, a \r before the \n becomes part of the line, so cat$ no longer matches “cat\r\n”.

boolean windows = Pattern.compile("(?m)cat$").matcher("cat\r\ndog").find();      // true
boolean unixOnly = Pattern.compile("(?md)cat$").matcher("cat\r\ndog").find();    // false
boolean separator = Pattern.compile("(?m)^dog").matcher("cat\u2028dog").find();  // true

String[] allBreaks = "a\r\nb\nc\rd".split("\\R");    // [a, b, c, d]
String[] lineFeedOnly = "a\r\nb".split("\n");        // [a\r, b]

To split text into lines or to match “a line break” inside a pattern, we use \R. It matches any of the line breaks above as one unit, plus vertical tab and form feed, and it never splits a \r\n pair. Splitting on “\n” alone leaves a stray \r at the end of each line of a Windows file, as the last line shows.

9. The Example Project on GitHub

The anchors example on GitHub prints the result of every snippet above, and its JUnit 6 tests check each one, including the trailing line break cases. The project builds with Maven on Java 25.

mvn -q compile exec:java
mvn test

10. Regex End of Line and Start of Line FAQs

Line anchors raise follow-up questions about empty lines and trailing spaces, and about whether matches() needs the anchors at all. More pattern topics are in the Java regex tutorial.

10.1. How Do I Match an Empty Line in Java?

We use (?m)^$. In MULTILINE mode, the caret and the dollar sign meet at the same position on an empty line. Files with Windows line breaks leave a \r on each line, so (?m)^\r?$ or splitting with \R first is safer.

long emptyLines = Pattern.compile("(?m)^$").matcher("a\n\nb\n\nc").results().count();   // 2

10.2. How Do I Remove Trailing Spaces From Every Line?

We replace (?m)[ \t]+$ with an empty string. The MULTILINE flag makes $ match at each line end, and the class [ \t] keeps the line breaks themselves. Since Java 11, String.stripTrailing() does the same for a single line.

String trimmed = "a  \nb\t\nc ".replaceAll("(?m)[ \\t]+$", "");   // "a\nb\nc"

10.3. How Do I Find Lines That Do Not End With a Word?

We combine the anchors with a negative lookahead. The pattern (?m)^(?!.*ing$).*$ matches each line where “ing” is not at the end.

List<String> other = Pattern.compile("(?m)^(?!.*ing$).*$").matcher("singing\nbread\nring").results().map(MatchResult::group).toList();   // [bread]

10.4. Do I Need ^ and $ With String.matches()?

No. The method String.matches() succeeds only when the whole string matches, so the anchors are redundant there. They do no harm, but leaving them out keeps the pattern shorter. We add them only for find(), as section 4 shows.

11. Conclusion

The caret ^ anchors a pattern to the start of a string and the dollar sign $ anchors it to the end. With MULTILINE or (?m), both also match at every line, which is how we search a multi-line text line by line.

For validation, matches() or the pair \A and \z is safer than ^ and $, because $ accepts a trailing line break. When we only need a fixed prefix or suffix, startsWith() and endsWith() are the simpler choice. The Java regex metacharacters reference lists the other anchors, and the word boundary article covers \b.

12. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.