1. 概述
字符串替换是 Java 中常见的操作之一。
得益于 String
类中提供的 replaceAll()
方法,我们可以很方便地结合正则表达式进行文本替换。但有些正则写法容易让人踩坑,比如 \\s
和 \\s+
看似相似,实际行为却大不相同。
本文将通过具体示例,帮你彻底搞清这两个表达式的差异,避免日后在空格处理上翻车。
2. \s 与 \s+ 的核心区别
我们先拆解这两个正则的含义:
\\s
:是一个预定义的字符类,匹配单个空白字符。包括以下字符:[ \t\n\x0B\f\r]
也就是:空格、制表符(
\t
)、换行(\n
)、垂直制表符(\x0B
)、换页符(\f
)、回车(\r
)。+
:是“贪婪量词”,表示前面的模式至少出现一次或多次。例如X+
会匹配一个或多个连续的X
。
✅ 因此结论很明确:
\\s
匹配一个空白字符;\\s+
匹配一个或多个连续的空白字符。
关键区别在于:\\s+
会把一连串空格当作“一整个匹配单元”来处理,而 \\s
是逐个字符匹配。
3. 使用非空替换时的行为对比
我们用一个典型字符串作为输入,观察两者差异:
String INPUT_STR = "Text With Whitespaces! ";
这个字符串中包含多处连续空格,总共 11 个空白字符。
3.1 使用 \\s
替换
String result = INPUT_STR.replaceAll("\\s", "_");
assertEquals("Text___With_____Whitespaces!___", result);
⚠️ 每个空白字符都被单独替换为 _
,结果中出现了多个连续下划线。
3.2 使用 \\s+
替换
String result = INPUT_STR.replaceAll("\\s+", "_");
assertEquals("Text_With_Whitespaces!_", result);
✅ \\s+
将每一段连续空白视为一个整体,只替换成一个 _
,结果更干净。
📌 总结:
正则表达式 | 匹配单位 | 替换次数 | 结果特点 |
---|---|---|---|
\\s |
单个空白字符 | 11 次 | 连续下划线 |
\\s+ |
连续空白序列 | 3 次 | 每段空白只留一个符号 |
4. 使用空字符串替换(删除空白)
更常见的需求是“删除多余空白”,也就是替换为空字符串 ""
。
4.1 使用 \\s
String result1 = INPUT_STR.replaceAll("\\s", "");
assertEquals("TextWithWhitespaces!", result1);
逐个删除每个空白字符,共执行 11 次替换。
4.2 使用 \\s+
String result2 = INPUT_STR.replaceAll("\\s+", "");
assertEquals("TextWithWhitespaces!", result2);
将每段连续空白整体删除,共执行 3 次替换。
✅ 最终结果完全一样,但:
\\s+
更高效!因为它执行的替换次数更少。
虽然现代 JVM 下性能差距可能不大,但在高频调用或大数据量场景下,\\s+
明显更优。
5. 实际开发建议
- ✅ 去重空白、格式化文本:优先使用
\\s+
,简单粗暴,结果可控。 - ✅ 需要精确控制每个空格(如日志解析):才考虑用
\\s
。 - ⚠️ 不要写成
\\s*
,它会匹配“零个或多个空白”,可能导致意外结果(比如在每个字符间插入替换内容)。
6. 结论
\\s
和\\s+
虽然只差一个+
,但语义完全不同。- 在
replaceAll()
中,\\s+
通常更实用,尤其是在处理“多余空白”时。 - 理解贪婪量词的行为,能帮你写出更高效、更安全的正则表达式。
示例代码已上传至 GitHub:https://github.com/example/java-regex-demo