1. 概述
字符串操作是 Java 开发中的核心技能之一,而 String.replaceAll()
方法则是处理文本替换的利器。本文将深入解析这个方法的工作原理,并通过实际案例展示其强大功能。
2. String.replaceAll() 方法解析
replaceAll()
允许我们通过正则表达式匹配字符串中的模式,并用指定内容替换匹配项。它本质上是正则表达式驱动的替换工具,而非简单的文本替换。
方法签名如下:
public String replaceAll(String regex, String replacement)
参数说明:
regex
:用于匹配的正则表达式replacement
:替换匹配项的字符串
基础示例:
String input = "Hello w o r l d";
String result = input.replaceAll("\\s", "_");
assertEquals("Hello_w_o_r_l_d", result);
这里用 \\s
匹配所有空白字符,替换为下划线。
再看一个复杂示例:
result = input.replaceAll("e.*o", "X");
assertEquals("HX r l d", result);
正则 e.*o
会匹配从第一个 'e' 开始到最后一个 'o' 结束的所有字符(贪婪匹配)。若需非贪婪匹配(匹配到第一个 'o' 即停止):
result = input.replaceAll("e.*?o", "X"); // 非贪婪匹配
assertEquals("HX w o r l d", result);
result = input.replaceAll("e[^o]*o", "X"); // 等效写法
assertEquals("HX w o r_l_d", result);
⚠️ 注意:.*?
和 [^o]*
都能实现非贪婪匹配,但后者明确排除了 'o' 字符。
3. replace() vs replaceAll() 对比
虽然 replace()
和 replaceAll()
有时结果相同,但核心差异在于是否支持正则表达式:
String input = "hello.java.hello.world";
// 字面量替换(结果相同)
String replaceResult = input.replace("hello", "hi");
assertEquals("hi.java.hi.world", replaceResult);
String replaceAllResult = input.replaceAll("hello", "hi");
assertEquals("hi.java.hi.world", replaceAllResult);
但当处理特殊字符时差异明显:
// 替换点号时结果不同
replaceResult = input.replace(".", ":"); // 字面量替换
assertEquals("hello:java:hello:world", replaceResult);
replaceAllResult = input.replaceAll(".", ":"); // 正则替换(.匹配任意字符)
assertEquals("::::::::::::::::::::::", replaceAllResult);
❌ 踩坑提醒:replaceAll()
中的 .
是正则元字符,需转义处理!
4. 正则特殊字符处理
正则中的特殊字符(如 .
, []
, ()
, |
等)需要特殊处理才能作为字面量匹配。两种解决方案:
方案1:反斜杠转义
String input = "hi.java.hi.world";
String result = input.replaceAll("\\.", ":"); // 转义点号
assertEquals("hi:java:hi:world", result);
方案2:字符组包裹
result = input.replaceAll("[.]", ":"); // 用字符组包裹
assertEquals("hi:java:hi:world", result);
处理括号示例:
input = "(debug) hello.world";
// ❌ 错误:括号被当作分组符
result = input.replaceAll("(debug)", "[info]");
assertEquals("([info]) hello.world", result);
// ✅ 正确:转义或字符组处理
result = input.replaceAll("\\(debug\\)", "[info]");
assertEquals("[info] hello.world", result);
result = input.replaceAll("[(]debug[)]", "[info]");
assertEquals("[info] hello.world", result);
5. 无效正则的处理
当传入无效正则表达式时,replaceAll()
会抛出 PatternSyntaxException
:
String input = "Hello world";
assertThrows(PatternSyntaxException.class, () -> input.replaceAll("e**", "X"));
⚠️ 原因:e**
中第二个 *
缺少前置元素,属于非法正则语法。
6. 超越简单替换的高级用法
6.1 捕获组引用
通过 $n
引用捕获组实现复杂重组:
String input = "123456789";
String expected = "789-456-123";
// 数字分组引用
String result = input.replaceAll("(\\d{3})(\\d{3})(\\d{3})", "$3-$2-$1");
assertEquals(expected, result);
// 命名捕获组引用
result = input.replaceAll(
"(?<first>\\d{3})(?<second>\\d{3})(?<third>\\d{3})",
"${third}-${second}-${first}"
);
assertEquals(expected, result);
✅ 命名捕获组优势:提升复杂正则的可读性和可维护性。
6.2 零宽断言插入分隔符
使用零宽断言在字符间插入分隔符(不消耗字符):
String input = "abcdefg";
String expected = "a-b-c-d-e-f-g";
String result = input.replaceAll("(?<=.)(?=.)", "-");
assertEquals(expected, result);
正则解析:
(?<=.)
:正向后行断言(当前位置前有字符)(?=.)
:正向先行断言(当前位置后有字符)- 匹配位置:所有相邻字符之间的"间隙"
7. 总结
String.replaceAll()
是 Java 中强大的文本处理工具,其核心价值在于正则表达式的深度集成。通过本文我们掌握了:
- 基础替换与正则模式匹配
- 与
replace()
的本质区别 - 特殊字符的转义处理
- 捕获组引用和零宽断言等高级技巧
完整示例代码见 GitHub 仓库。