Java Matcher.appendReplacement Method
Last modified: April 20, 2025
The appendReplacement
method is part of Java's Matcher
class in the java.util.regex
package. It performs incremental
replacement operations on input strings while preserving non-matched portions.
This method is typically used with appendTail
to build modified
strings where only specific matches are replaced. It offers more control than
simple replace methods by allowing custom processing of each match.
Basic Definitions
Matcher.appendReplacement(StringBuffer sb, String replacement)
appends text to a string buffer and performs replacements. It reads the input
text up to the current match and appends it to the buffer, then appends the
replacement string.
The method requires a StringBuffer
to accumulate results and a
replacement string that can reference captured groups. It throws
IllegalStateException
if no match has been attempted or if the
previous match failed.
Basic Replacement Example
This example demonstrates the fundamental usage of appendReplacement
to replace all occurrences of a pattern in a string. We'll replace all digits
with their word equivalents.
package com.zetcode; import java.util.regex.*; public class BasicReplacement { 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 replacement = convertToWord(matcher.group()); matcher.appendReplacement(sb, replacement); } matcher.appendTail(sb); System.out.println("Original: " + input); System.out.println("Modified: " + sb.toString()); } private static String convertToWord(String number) { switch (number) { case "1": return "one"; case "2": return "two"; case "3": return "three"; case "4": return "four"; case "5": return "five"; default: return number; } } }
In this example, we first compile a pattern to match digits. The matcher finds
each digit sequence, and for each match, we call appendReplacement
with a custom replacement. Finally, appendTail
adds the remaining
text after the last match.
Using Group References in Replacement
This example shows how to reference captured groups in the replacement string. We'll reformat dates from "MM/DD/YYYY" to "YYYY-MM-DD" format.
package com.zetcode; import java.util.regex.*; public class GroupReference { public static void main(String[] args) { String input = "Dates: 12/25/2023, 01/01/2024, 07/04/2023"; Pattern pattern = Pattern.compile("(\\d{2})/(\\d{2})/(\\d{4})"); Matcher matcher = pattern.matcher(input); StringBuffer sb = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(sb, "$3-$1-$2"); } matcher.appendTail(sb); System.out.println("Original: " + input); System.out.println("Modified: " + sb.toString()); } }
Here we use group references ($1, $2, $3) in the replacement string to rearrange the date components. The pattern captures month, day, and year as separate groups which we then reference in the replacement string.
Conditional Replacement
This example demonstrates conditional replacement based on match content. We'll replace words with their lengths, but only if they're longer than 3 characters.
package com.zetcode; import java.util.regex.*; public class ConditionalReplacement { public static void main(String[] args) { String input = "The quick brown fox jumps over the lazy dog"; Pattern pattern = Pattern.compile("\\w+"); Matcher matcher = pattern.matcher(input); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String word = matcher.group(); String replacement = word.length() > 3 ? String.valueOf(word.length()) : word; matcher.appendReplacement(sb, replacement); } matcher.appendTail(sb); System.out.println("Original: " + input); System.out.println("Modified: " + sb.toString()); } }
The code checks each matched word's length and replaces it with its character
count only if it's longer than 3 characters. This shows how appendReplacement
enables complex replacement logic that simple replace methods can't handle.
HTML Tag Conversion
This example converts simple markdown-style formatting to HTML tags. We'll replace *bold* text with <b>bold</b> and _italic_ with <i>italic</i>.
package com.zetcode; import java.util.regex.*; public class HtmlConversion { public static void main(String[] args) { String input = "This is *bold* and this is _italic_ text."; Pattern boldPattern = Pattern.compile("\\*(.*?)\\*"); Pattern italicPattern = Pattern.compile("_(.*?)_"); StringBuffer sb = new StringBuffer(); Matcher matcher = boldPattern.matcher(input); // First replace bold while (matcher.find()) { matcher.appendReplacement(sb, "<b>$1</b>"); } matcher.appendTail(sb); // Then replace italic in the modified string String intermediate = sb.toString(); matcher = italicPattern.matcher(intermediate); sb = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(sb, "<i>$1</i>"); } matcher.appendTail(sb); System.out.println("Original: " + input); System.out.println("Modified: " + sb.toString()); } }
This example shows two replacement passes - first for bold, then for italic formatting. We process the intermediate result of the first replacement in the second pass. Note the use of non-greedy quantifiers (.*?) to match the shortest possible text between markers.
Custom Replacement Logic
This example demonstrates complex replacement logic where we process each match before replacement. We'll obfuscate email addresses by replacing characters with asterisks except the first and last characters.
package com.zetcode; import java.util.regex.*; public class CustomLogic { public static void main(String[] args) { String input = "Contact us at support@example.com or sales@company.org"; Pattern pattern = Pattern.compile("\\b[\\w.%-]+@[\\w.-]+\\.[a-zA-Z]{2,6}\\b"); Matcher matcher = pattern.matcher(input); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String email = matcher.group(); String obfuscated = obfuscateEmail(email); matcher.appendReplacement(sb, obfuscated); } matcher.appendTail(sb); System.out.println("Original: " + input); System.out.println("Modified: " + sb.toString()); } private static String obfuscateEmail(String email) { String[] parts = email.split("@"); String local = parts[0]; String domain = parts[1]; // Obfuscate local part (keep first and last character) if (local.length() > 2) { local = local.charAt(0) + "*".repeat(local.length() - 2) + local.charAt(local.length() - 1); } return local + "@" + domain; } }
The example shows how appendReplacement
can be used with custom
processing logic for each match. We split the email address, process the local
part, and then combine it back for the replacement.
Multiline Replacement
This example demonstrates handling multiline input with appendReplacement
.
We'll add line numbers to each line in a multiline string.
package com.zetcode; import java.util.regex.*; public class MultilineProcessing { public static void main(String[] args) { String input = "First line\nSecond line\nThird line\nFourth line"; Pattern pattern = Pattern.compile("^.*$", Pattern.MULTILINE); Matcher matcher = pattern.matcher(input); StringBuffer sb = new StringBuffer(); int lineNumber = 1; while (matcher.find()) { String replacement = lineNumber + ": " + matcher.group(); matcher.appendReplacement(sb, replacement); lineNumber++; } matcher.appendTail(sb); System.out.println("Original:\n" + input); System.out.println("\nModified:\n" + sb.toString()); } }
The key here is using the Pattern.MULTILINE
flag to make ^ and $
match at the start and end of each line. We maintain a counter to number each
line as we process it with appendReplacement
.
Complex Pattern with Backreferences
This advanced example shows using backreferences in both the pattern and replacement. We'll find duplicate words and mark them in the output.
package com.zetcode; import java.util.regex.*; public class DuplicateWords { public static void main(String[] args) { String input = "This this is a test test of duplicate word detection."; Pattern pattern = Pattern.compile("\\b(\\w+)(\\s+\\1\\b)+", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(input); StringBuffer sb = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(sb, "[DUPLICATE: $1]"); } matcher.appendTail(sb); System.out.println("Original: " + input); System.out.println("Modified: " + sb.toString()); } }
The pattern \b(\w+)(\s+\1\b)+
matches repeated words. The
replacement uses $1 to reference the first captured group (the repeated word).
The CASE_INSENSITIVE
flag ensures case differences don't prevent
detection of duplicates.
Source
Java Matcher.appendReplacement Documentation
This tutorial has covered various uses of Matcher.appendReplacement
,
from basic to advanced scenarios. The method provides powerful string
manipulation capabilities when combined with regular expressions.
Author
List all Java tutorials.