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


原始标题:Regular Expressions \s and \s+ in Java