String.replaceAll()
用正则表达式替换所有匹配项
方法定义
public String replaceAll(String regex, String replacement)
参数说明:
regex
:用于匹配的正则表达式replacement
:替换匹配项的字符串
返回值:
- 返回一个新字符串,其中所有与正则表达式匹配的部分都被替换
- 原始字符串保持不变(字符串是不可变的)
功能说明
replaceAll()
方法使用正则表达式查找字符串中所有匹配的部分,并用指定的替换字符串替换它们。这是与 replace()
方法的关键区别:
replace()
:按字面值替换,不使用正则表达式replaceAll()
:使用正则表达式进行模式匹配
重要特性:
- 返回新字符串,原字符串不变
- 替换所有匹配项(不仅仅是第一个)
- 正则表达式会被编译和匹配
- 替换字符串中的
$
和\
有特殊含义
示例代码
基本用法
public class ReplaceAllExample {
public static void main(String[] args) {
String text = "Hello world, hello Java, HELLO Programming";
// 忽略大小写的替换
String result1 = text.replaceAll("(?i)hello", "Hi");
System.out.println(result1);
// "Hi world, Hi Java, Hi Programming"
// 替换所有数字
String numberText = "Phone: 123-456-7890, Age: 25";
String result2 = numberText.replaceAll("\\d", "*");
System.out.println(result2);
// "Phone: ***-***-****, Age: **"
// 替换所有空白字符
String messyText = " Hello World \t\n Java ";
String result3 = messyText.replaceAll("\\s+", " ");
System.out.println("'" + result3 + "'");
// " Hello World Java "
}
}
常见应用场景
// 1. 清理文本:移除特殊字符
String dirtyText = "User@name#123!password$";
String cleanText = dirtyText.replaceAll("[^a-zA-Z0-9]", "");
System.out.println(cleanText); // "Username123password"
// 2. 格式化电话号码
String phone = "123-456-7890";
String formatted = phone.replaceAll("(\\d{3})-(\\d{3})-(\\d{4})", "($1) $2-$3");
System.out.println(formatted); // "(123) 456-7890"
// 3. HTML 转义字符处理
String html = "<p>Hello & World</p>";
String escaped = html.replaceAll("[<>&]",
match -> {
switch (match) {
case "<": return "<";
case ">": return ">";
case "&": return "&";
default: return match;
}
});
// 注意:上面是 Java 8+ 的方式,传统方式需要多次 replaceAll
// 4. 邮箱地址模糊处理
String email = "john.doe@example.com";
String masked = email.replaceAll("(?<=.).(?=.*@)", "*");
System.out.println(masked); // "j***n.d**@example.com"
使用捕获组
// 日期格式转换:YYYY-MM-DD to DD/MM/YYYY
String date = "2023-12-25";
String newFormat = date.replaceAll("(\\d{4})-(\\d{2})-(\\d{2})", "$3/$2/$1");
System.out.println(newFormat); // "25/12/2023"
// 交换字符串中的两个部分
String name = "Doe, John";
String formattedName = name.replaceAll("(\\w+),\\s+(\\w+)", "$2 $1");
System.out.println(formattedName); // "John Doe"
使用技巧
1. 转义特殊字符
// 错误:点号在正则中表示任意字符
// String result = "file.txt".replaceAll(".", "X"); // 返回 "XXXXX"
// 正确:转义点号
String result = "file.txt".replaceAll("\\.", "X");
System.out.println(result); // "fileXtxt"
// 需要转义的字符:. [ { ( ) * + ? ^ $ \ |
String escaped = "a.b*c".replaceAll("[.\\*]", "X");
System.out.println(escaped); // "aXbXc"
2. 处理替换字符串中的特殊字符
// 替换字符串中的 $ 和 \ 需要特殊处理
String text = "Price: $100";
// 错误:$1 被解释为捕获组
// String result = text.replaceAll("\\$(\\d+)", "€$1"); // 可能出错
// 正确:使用 Matcher.quoteReplacement()
String euro = Matcher.quoteReplacement("€") + "$1";
String result = text.replaceAll("\\$(\\d+)", euro);
System.out.println(result); // "Price: €100"
// 或者使用 replace() 方法(如果不需要正则)
String simpleReplace = text.replace("$", "€");
3. 忽略大小写替换
// 使用 (?i) 标志进行忽略大小写匹配
String text = "Java JAVA java";
String result = text.replaceAll("(?i)java", "Python");
System.out.println(result); // "Python Python Python"
4. 限制替换次数的技巧
// replaceAll() 替换所有,如果只想替换前N个,需要自定义方法
public static String replaceFirstN(String text, String regex, String replacement, int n) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
StringBuffer sb = new StringBuffer();
int count = 0;
while (matcher.find() && count < n) {
matcher.appendReplacement(sb, replacement);
count++;
}
matcher.appendTail(sb);
return sb.toString();
}
// 使用示例
String text = "a-b-c-d-e";
String result = replaceFirstN(text, "-", "_", 2);
System.out.println(result); // "a_b_c-d-e"
常见错误
1. 忘记转义正则特殊字符
// 错误示例
String result = "1.5 + 2.3 = 3.8".replaceAll(".", "X");
// 结果是 "XXXXXXXXXXXXX",因为 . 匹配所有字符
// 正确做法
String result = "1.5 + 2.3 = 3.8".replaceAll("\\.", "X");
// 结果是 "1X5 + 2X3 = 3X8"
2. 替换字符串中的 $
问题
// 错误:$ 被解释为捕获组
String text = "The price is $10";
// String result = text.replaceAll("\\$(\\d+)", "Cost: $1"); // 可能抛出异常
// 正确:使用 Matcher.quoteReplacement()
String replacement = Matcher.quoteReplacement("Cost: $") + "$1";
String result = text.replaceAll("\\$(\\d+)", replacement);
3. 性能问题:在循环中使用
// 不推荐:在循环中重复编译正则表达式
List<String> texts = Arrays.asList("...", "...");
for (String text : texts) {
String result = text.replaceAll("\\d+", "X"); // 每次都编译
}
// 推荐:预编译模式
Pattern pattern = Pattern.compile("\\d+");
for (String text : texts) {
String result = pattern.matcher(text).replaceAll("X");
}
4. 空指针异常
// 错误:null 字符串调用方法
// String nullText = null;
// nullText.replaceAll("a", "b"); // NullPointerException
// 正确:添加 null 检查
public static String safeReplaceAll(String text, String regex, String replacement) {
if (text == null) return null;
return text.replaceAll(regex, replacement);
}
注意事项
- 正则表达式语法:确保正则表达式语法正确,否则会抛出
PatternSyntaxException
- 性能考虑:正则表达式编译有开销,频繁使用时考虑预编译
- 内存使用:会产生新字符串对象,大量操作时注意内存
- 线程安全:字符串本身是不可变的,但正则模式可以共享
- 特殊字符:注意替换字符串中
$
和\
的特殊含义
最佳实践
1. 预编译正则表达式
// 对于频繁使用的正则表达式
private static final Pattern EMAIL_PATTERN = Pattern.compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}");
public static String maskEmail(String email) {
return EMAIL_PATTERN.matcher(email).replaceAll("****@****.com");
}
2. 创建工具方法
public class StringUtils {
// 安全的 replaceAll 方法
public static String safeReplaceAll(String text, String regex, String replacement) {
if (text == null || regex == null || replacement == null) {
return text;
}
try {
return text.replaceAll(regex, replacement);
} catch (PatternSyntaxException e) {
// 处理正则表达式错误
System.err.println("Invalid regex: " + regex);
return text;
}
}
// 转义文本用于正则表达式
public static String escapeForRegex(String text) {
return Pattern.quote(text);
}
}
3. 使用 StringBuilder 进行多次替换
// 对于多个替换操作
public static String multiReplace(String text, Map<String, String> replacements) {
StringBuilder sb = new StringBuilder(text);
for (Map.Entry<String, String> entry : replacements.entrySet()) {
String regex = Pattern.quote(entry.getKey()); // 转义字面值
String escapedReplacement = Matcher.quoteReplacement(entry.getValue());
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(sb);
sb = new StringBuilder();
while (matcher.find()) {
matcher.appendReplacement(sb, escapedReplacement);
}
matcher.appendTail(sb);
}
return sb.toString();
}
4. 验证输入
public static boolean isValidRegex(String regex) {
try {
Pattern.compile(regex);
return true;
} catch (PatternSyntaxException e) {
return false;
}
}
性能优化
1. 预编译模式对象
// 创建静态模式对象
private static final Pattern DIGIT_PATTERN = Pattern.compile("\\d+");
private static final Pattern WORD_PATTERN = Pattern.compile("\\w+");
// 使用预编译的模式
public static String processText(String text) {
return DIGIT_PATTERN.matcher(text).replaceAll("#")
.replaceAll(WORD_PATTERN, "WORD");
}
2. 对于简单替换,考虑使用 replace()
// 如果只是字面值替换,使用 replace() 更高效
String text = "Hello world";
// 使用 replace() - 更快
String result1 = text.replace("world", "Java");
// 使用 replaceAll() - 较慢(需要正则编译)
String result2 = text.replaceAll("world", "Java");
3. 批量处理
// 对于大量文本的相同替换
public static List<String> batchReplace(List<String> texts, String regex, String replacement) {
Pattern pattern = Pattern.compile(regex);
return texts.parallelStream() // 使用并行流
.map(text -> pattern.matcher(text).replaceAll(replacement))
.collect(Collectors.toList());
}
总结
String.replaceAll()
是 Java 中强大的字符串替换工具,关键要点:
- 核心功能:使用正则表达式替换所有匹配项
- 正则表达式:需要正确理解和转义特殊字符
- 不可变性:返回新字符串,原字符串不变
- 特殊字符:注意替换字符串中
$
和\
的特殊含义 - 最佳实践:
- 正确转义正则特殊字符
- 对于频繁操作,预编译正则表达式
- 处理异常情况和边界条件
- 考虑性能优化
掌握 replaceAll()
方法能有效处理各种复杂的字符串替换需求,特别是在数据清洗、格式化和文本处理场景中非常有用。