Java Matcher.appendTail Method
Last modified: April 20, 2025
The appendTail
method is part of Java's java.util.regex.Matcher
class. It appends the remaining input sequence to a StringBuffer after the last
match. This method is typically used with appendReplacement
for
regex-based string manipulation.
When performing find-and-replace operations with regex, appendTail
ensures any text after the last match is included in the result. It completes
the transformation process started by appendReplacement
calls.
Matcher.appendTail Overview
The appendTail
method has the signature:
public StringBuffer appendTail(StringBuffer sb)
. It appends the
remaining substring to the specified StringBuffer and returns the buffer.
This method is essential when using appendReplacement
in a loop.
After processing all matches, appendTail
adds any remaining text
that follows the last match. Without it, this text would be lost.
Basic appendTail Usage
This example demonstrates the fundamental usage of appendTail
with
appendReplacement
. We'll replace all digits in a string with their
word equivalents while preserving the rest of the text.
package com.zetcode; import java.util.regex.*; public class AppendTailBasic { public static void main(String[] args) { String input = "I have 3 apples and 5 oranges."; Pattern pattern = Pattern.compile("\\d+"); Matcher matcher = pattern.matcher(input); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String numWord = convertToWord(matcher.group()); matcher.appendReplacement(sb, numWord); } matcher.appendTail(sb); System.out.println("Original: " + input); System.out.println("Modified: " + sb.toString()); } private static String convertToWord(String num) { switch(num) { case "3": return "three"; case "5": return "five"; default: return num; } } }
In this example, we first find all digit sequences using matcher.find
.
For each match, we convert the digit to its word form and use
appendReplacement
. After the loop, appendTail
adds
the remaining text (" oranges.") to the result.
The output shows the complete transformed string with all digits replaced and
the remaining text preserved. Without appendTail
, the final part
of the string would be missing.
Multiple Replacement with appendTail
This example shows how to perform multiple replacements in a text while maintaining the original structure. We'll replace both dates and currency values in a financial statement.
package com.zetcode; import java.util.regex.*; public class MultipleReplacements { public static void main(String[] args) { String statement = "Date: 12/31/2023, Amount: $1,250.75\n" + "Date: 01/15/2024, Amount: $950.50"; Pattern datePattern = Pattern.compile("\\d{2}/\\d{2}/\\d{4}"); Pattern amountPattern = Pattern.compile("\\$\\d{1,3}(,\\d{3})*\\.\\d{2}"); Matcher matcher = datePattern.matcher(statement); StringBuffer sb = new StringBuffer(); // Replace dates first while (matcher.find()) { String newDate = formatDate(matcher.group()); matcher.appendReplacement(sb, newDate); } matcher.appendTail(sb); // Now replace amounts in the modified string String intermediate = sb.toString(); matcher = amountPattern.matcher(intermediate); sb = new StringBuffer(); while (matcher.find()) { String newAmount = formatAmount(matcher.group()); matcher.appendReplacement(sb, newAmount); } matcher.appendTail(sb); System.out.println("Original:\n" + statement); System.out.println("\nModified:\n" + sb.toString()); } private static String formatDate(String date) { return date.replaceAll("(\\d{2})/(\\d{2})/(\\d{4})", "$2-$1-$3"); } private static String formatAmount(String amount) { return amount.replace("$", "").replace(",", "") + " USD"; } }
This example performs two sequential replacement operations. First, it reformats dates from MM/DD/YYYY to DD-MM-YYYY. Then it converts currency amounts from $1,250.75 format to 1250.75 USD format.
After each replacement pass, appendTail
ensures all remaining text
is preserved. The intermediate results are stored and used as input for the next
replacement operation.
appendTail with Partial Replacement
Sometimes we want to replace only some matches while leaving others unchanged.
This example demonstrates selective replacement while still using
appendTail
to maintain the complete output.
package com.zetcode; import java.util.regex.*; public class SelectiveReplacement { public static void main(String[] args) { String text = "The colors are red, blue, green, and yellow. " + "I prefer red and blue over green and yellow."; Pattern colorPattern = Pattern.compile("red|blue|green|yellow"); Matcher matcher = colorPattern.matcher(text); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String color = matcher.group(); // Only replace 'red' and 'blue' with their French equivalents if (color.equals("red") || color.equals("blue")) { String replacement = color.equals("red") ? "rouge" : "bleu"; matcher.appendReplacement(sb, replacement); } } matcher.appendTail(sb); System.out.println("Original: " + text); System.out.println("Modified: " + sb.toString()); } }
In this example, we only replace "red" and "blue" with their French equivalents
while leaving "green" and "yellow" unchanged. The appendTail
call
ensures all text after the last match (whether replaced or not) is included.
This demonstrates how appendTail
works even when not all matches
are replaced. The method simply appends any remaining text after the last match
position, regardless of whether replacements occurred.
HTML Tag Processing with appendTail
This example shows how to use appendTail
when processing HTML
content. We'll remove all HTML tags while preserving the text content.
package com.zetcode; import java.util.regex.*; public class HtmlTagRemoval { public static void main(String[] args) { String html = "<html><body><h1>Title</h1>" + "<p>Paragraph with <b>bold</b> text.</p></body></html>"; Pattern tagPattern = Pattern.compile("<[^>]+>"); Matcher matcher = tagPattern.matcher(html); StringBuffer sb = new StringBuffer(); while (matcher.find()) { // Replace each tag with empty string matcher.appendReplacement(sb, ""); } matcher.appendTail(sb); System.out.println("Original HTML:\n" + html); System.out.println("\nText content:\n" + sb.toString()); } }
This code removes all HTML tags from a string while preserving the text content. The regex pattern matches any text between angle brackets. Each match is replaced with an empty string.
The appendTail
call ensures any text after the last HTML tag is
included in the result. This is crucial as HTML content often has text after
the final closing tag.
appendTail with Custom Transformations
This example demonstrates using appendTail
with complex
transformations. We'll convert a markdown-like syntax to HTML, showing how
appendTail
handles the complete transformation.
package com.zetcode; import java.util.regex.*; public class MarkdownToHtml { public static void main(String[] args) { String markdown = "# Heading\n" + "This is *italic* and this is **bold**.\n" + "Visit [ZetCode](https://zetcode.com)."; // Process headings Pattern headingPattern = Pattern.compile("^# (.*)$", Pattern.MULTILINE); Matcher matcher = headingPattern.matcher(markdown); StringBuffer sb = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(sb, "<h1>" + matcher.group(1) + "</h1>"); } matcher.appendTail(sb); String step1 = sb.toString(); // Process italics and bold sb = new StringBuffer(); Pattern emphasisPattern = Pattern.compile("\\*(\\*?)(.*?)\\1\\*"); matcher = emphasisPattern.matcher(step1); while (matcher.find()) { String tag = matcher.group(1).isEmpty() ? "em" : "strong"; matcher.appendReplacement(sb, "<" + tag + ">" + matcher.group(2) + "</" + tag + ">"); } matcher.appendTail(sb); String step2 = sb.toString(); // Process links sb = new StringBuffer(); Pattern linkPattern = Pattern.compile("\\[(.*?)\\]\\((.*?)\\)"); matcher = linkPattern.matcher(step2); while (matcher.find()) { matcher.appendReplacement(sb, "<a href=\"" + matcher.group(2) + "\">" + matcher.group(1) + "</a>"); } matcher.appendTail(sb); System.out.println("Original Markdown:\n" + markdown); System.out.println("\nConverted HTML:\n" + sb.toString()); } }
This example performs multiple transformations in sequence: converting headings,
emphasis (italic and bold), and links from markdown-like syntax to HTML. Each
transformation uses appendReplacement
and appendTail
.
The intermediate results are passed between transformations, with
appendTail
ensuring no content is lost between steps. This
demonstrates how to build complex text processors using these methods.
appendTail in Template Processing
This example shows how appendTail
can be used in template
processing. We'll replace placeholders in a template with actual values while
preserving the template structure.
package com.zetcode; import java.util.regex.*; import java.util.*; public class TemplateProcessor { public static void main(String[] args) { String template = "Dear {{customer}},\n\n" + "Your order #{{orderId}} for {{product}} has shipped.\n" + "Expected delivery date: {{deliveryDate}}.\n\n" + "Thank you for shopping with us!"; Map<String, String> values = new HashMap<>(); values.put("customer", "John Smith"); values.put("orderId", "12345"); values.put("product", "Java Programming Book"); values.put("deliveryDate", "April 25, 2025"); Pattern placeholderPattern = Pattern.compile("\\{\\{(.*?)\\}\\}"); Matcher matcher = placeholderPattern.matcher(template); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String key = matcher.group(1); String replacement = values.getOrDefault(key, "{{" + key + "}}"); matcher.appendReplacement(sb, replacement); } matcher.appendTail(sb); System.out.println("Processed Template:\n" + sb.toString()); } }
This code processes a template with placeholders in {{key}}
format.
Each placeholder is replaced with its corresponding value from a map. If a key
isn't found, the placeholder remains unchanged.
The appendTail
call ensures all template text after the last
placeholder is included in the output. This is essential for maintaining the
complete message structure, including closing text and formatting.
appendTail Performance Considerations
This example demonstrates the performance implications of using
appendTail
versus alternative approaches. We'll compare string
building techniques for large text processing.
package com.zetcode; import java.util.regex.*; public class PerformanceComparison { public static void main(String[] args) { // Generate a large string with many numbers StringBuilder bigText = new StringBuilder(); for (int i = 0; i < 10000; i++) { bigText.append("Number ").append(i).append(", "); } String input = bigText.toString(); // Method 1: Using appendReplacement/appendTail long start1 = System.currentTimeMillis(); Pattern pattern = Pattern.compile("\\d+"); Matcher matcher = pattern.matcher(input); StringBuffer sb1 = new StringBuffer(); while (matcher.find()) { String replacement = "NUM" + matcher.group(); matcher.appendReplacement(sb1, replacement); } matcher.appendTail(sb1); long end1 = System.currentTimeMillis(); // Method 2: Using replaceAll long start2 = System.currentTimeMillis(); String result2 = input.replaceAll("\\d+", "NUM$0"); long end2 = System.currentTimeMillis(); System.out.println("appendReplacement/appendTail time: " + (end1 - start1) + " ms"); System.out.println("replaceAll time: " + (end2 - start2) + " ms"); System.out.println("\nFirst 100 chars of appendTail result:\n" + sb1.substring(0, Math.min(100, sb1.length()))); System.out.println("\nFirst 100 chars of replaceAll result:\n" + result2.substring(0, Math.min(100, result2.length()))); } }
This example compares the performance of using appendReplacement
and appendTail
versus replaceAll
for a large input
string. The input contains many numbers that we prefix with "NUM". The
appendTail
approach allows for custom processing of each match but
may be slower than replaceAll
for simple replacements.
The timing results show the trade-offs between flexibility and performance.
appendReplacement
with appendTail
is more versatile
for complex transformations, while replaceAll
is optimized for
straightforward replacements. Developers should choose based on their specific
needs.
Source
Java Matcher.appendTail Documentation
In this article, we've explored the Matcher.appendTail
method in
depth with practical examples. This method, paired with
appendReplacement
, enables powerful regex-based text manipulation
in Java, from simple replacements to complex template processing.
Author
List all Java tutorials.