我们将从最简单的方法开始,逐步深入到更复杂和灵活的场景。
核心方法:String.replaceAll()
这是最常用、最直接的字符串替换方法。
语法
public String replaceAll(String regex, String replacement)
参数说明
regex: 一个正则表达式,用于匹配字符串中的模式。replacement: 一个字符串,用于替换所有匹配到的部分。
特点
- 全局替换:
replaceAll会替换字符串中所有匹配正则表达式的子串。 - 正则表达式:
regex参数是正则表达式,这意味着其中的一些字符(如 , , , , 等)有特殊含义,如果你只想替换一个固定的字符串,需要用Pattern.quote()对其进行转义,或者手动对特殊字符进行转义(在前面加\,但在 Java 字符串中需要写成\\)。
基础示例
示例 1:简单的文本替换
将字符串中的 "apple" 替换为 "orange"。
String text = "I like apple and apple pie.";
String replacedText = text.replaceAll("apple", "orange");
System.out.println(replacedText);
// 输出: I like orange and orange pie.
示例 2:使用正则表达式进行替换
将字符串中所有的数字替换为 "NUM"。
String text = "My phone number is 123-456-7890, and my ID is 987.";
String replacedText = text.replaceAll("\\d+", "NUM");
System.out.println(replacedText);
// 输出: My phone number is NUM-NUM-NUM, and my ID is NUM.
注意:\d 在正则中表示 "数字",但在 Java 字符串中 \ 是一个转义字符,所以需要写成 \\d 来表示一个字面量 \,再跟上 d。
示例 3:转义特殊字符
假设你想把字符串中的点 替换为 。 在正则中代表"任意字符",所以直接使用会出错。
String text = "file.name.txt";
// 错误示范: 这会把所有的字符都替换成 "file-name-txt"
// String wrongReplaced = text.replaceAll(".", "-");
// 正确做法1: 使用 Pattern.quote() 对要匹配的字符串进行转义
String correctReplaced1 = text.replaceAll(Pattern.quote("."), "-");
System.out.println(correctReplaced1);
// 输出: file-name-txt
// 正确做法2: 手动转义
String correctReplaced2 = text.replaceAll("\\.", "-");
System.out.println(correctReplaced2);
// 输出: file-name-txt
Pattern.quote() 是一个非常好的工具,它会自动为你生成一个可以安全匹配任意字符串的正则表达式,避免了手动转义的麻烦。
高级用法:Matcher.replaceAll()
当你需要对匹配结果进行更复杂的处理,或者需要多次使用同一个正则表达式时,使用 Matcher 类会更高效和灵活。
步骤
- 编译正则表达式:使用
Pattern.compile()创建一个Pattern对象。 - 创建匹配器:使用
pattern.matcher(text)创建一个Matcher对象。 - 执行替换:调用
matcher.replaceAll(replacement)。
示例:使用反向引用
反向引用允许你引用正则表达式中的捕获组(用括号 定义),这在格式化数据时非常有用。
假设我们想将 "Name: John, Age: 30" 这样的格式,转换为 "John is 30 years old"。
import java.util.regex.*;
String text = "Name: John, Age: 30";
// 正则表达式:
// (Name:\s*) -> 捕获组1: "Name: " (包括空格)
// ([A-Za-z]+) -> 捕获组2: "John"
// (,\s*Age:\s*) -> 捕获组3: ", Age: "
// (\d+) -> 捕获组4: "30"
Pattern pattern = Pattern.compile("(Name:\\s*)([A-Za-z]+)(,\\s*Age:\\s*)(\\d+)");
Matcher matcher = pattern.matcher(text);
// 替换字符串中:
// $1, $2, $3, $4 分别代表对应的捕获组
String replacement = "$2 is $4 years old"; // 注意:这里我们只用了组2和组4
String replacedText = matcher.replaceAll(replacement);
System.out.println(replacedText);
// 输出: John is 30 years old
反向引用语法:
$n:引用第n个捕获组。$0:引用整个匹配的字符串。
功能最强大的方法:Matcher.appendReplacement()
这是最灵活、功能最强大的替换方法,它允许你逐个处理匹配项,并在替换过程中保留未匹配的部分,这对于复杂的、需要“状态”的替换逻辑至关重要。
工作原理
appendReplacement(StringBuffer sb, String replacement):将上次匹配位置到当前匹配位置之间的文本,以及替换后的字符串,追加到StringBuffer中,匹配器的位置会更新到当前匹配的末尾。appendTail(StringBuffer sb):在所有匹配都处理完毕后,将剩余的文本追加到StringBuffer中。
示例:删除 HTML 标签并格式化
假设我们要处理 <p>Hello <b>World</b>!</p>,目标是得到 Hello World!,并且保留单词之间的空格。
import java.util.regex.*;
String html = "<p>Hello <b>World</b>!</p>";
// 正则表达式匹配任何 HTML 标签
// < : 匹配 '<'
// (?: : 非捕获组开始
// [^>] : 匹配一个非 '>' 的字符
// * : 重复零次或多次
// ) : 非捕获组结束
// > : 匹配 '>'
Pattern pattern = Pattern.compile("<(?:[^>]*)>");
Matcher matcher = pattern.matcher(html);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
// 将匹配到的标签替换为空字符串 ""
matcher.appendReplacement(sb, "");
}
// 添加剩余部分
matcher.appendTail(sb);
String cleanText = sb.toString();
System.out.println(cleanText);
// 输出: Hello World!
replaceFirst() 方法
如果你只想替换第一个匹配项,可以使用 replaceFirst()。
语法
public String replaceFirst(String regex, String replacement)
示例
String text = "apple banana apple cherry";
String replacedText = text.replaceFirst("apple", "orange");
System.out.println(replacedText);
// 输出: orange banana apple cherry
总结与对比
| 方法 | 来源 | 特点 | 适用场景 |
|---|---|---|---|
String.replaceAll() |
java.lang.String |
简单直接,全局替换。regex 不是固定字符串,需要转义。 |
简单的全局文本替换,快速实现。 |
String.replaceFirst() |
java.lang.lang.String |
只替换第一个匹配项。 | 只需要替换第一个出现的地方。 |
Matcher.replaceAll() |
java.util.regex.Matcher |
功能比 String.replaceAll() 强,可以使用反向引用 $n。 |
需要引用捕获组进行格式化替换。 |
Matcher.appendReplacement() |
java.util.regex.Matcher |
最强大、最灵活,可以逐个处理匹配,处理复杂逻辑,保留上下文。 | 复杂的替换逻辑,如删除标签、格式化文本、需要处理状态等。 |
最佳实践建议
- 优先使用
String.replaceAll():对于 90% 的简单替换需求,它是最简洁、最易读的选择。 - 处理固定字符串时使用
Pattern.quote():当你确定要匹配的是一个固定的、可能包含特殊字符的字符串时,用Pattern.quote()可以避免错误和麻烦。 - 需要反向引用时使用
Matcher.replaceAll():当你需要基于匹配到的不同部分(捕获组)来构建替换字符串时,Matcher是更好的选择。 - 处理复杂逻辑时使用
Matcher.appendReplacement():当你需要在一个循环中处理匹配项,并且替换逻辑依赖于前一个匹配的结果,或者需要精细控制输出流时,这是不二之选。
