应使用\p{z}|\s匹配所有空白符,其中\p{z}覆盖unicode分隔符(含中文全角空格、nbsp、行/段落分隔符),\s补充ascii空白,二者并集确保全面且逐字符替换,避免压缩。

replaceAll() 中的正则怎么写才能匹配所有空白符
Java 的 String.replaceAll() 第二个参数是替换字符串,第一个是正则表达式。要匹配“所有空白符”,不能只用空格 " " 或 "\s" —— 后者在 Java 中默认不匹配 Unicode 空白(比如中文全角空格、NBSP、行分隔符等),除非开启 UNICODE_CHARACTER_CLASS 模式。
最稳妥的一次性写法是:
str.replaceAll("\p{Z}|\s", " ")
\p{Z} 匹配 Unicode 中的所有分隔符(包括空格分隔符 Zs、行分隔符 Zl、段落分隔符 Zp),\s 覆盖 ASCII 常见空白( 、
、
、、 )。两者用 | 并集,基本覆盖全部常见空白字符。
为什么不用 \s+ 替换而要用单字符匹配
用 "\s+" 会把连续多个空白压缩成一个空格,但需求是“所有空白符替换为标准空格”,不是“归并空白”。比如 "au3000u00A0 b"(含中文空格、NBSP、制表符)若用 "\s+",中间三字符会被替换成单个 " ",结果是 "a b";而用 "\p{Z}|\s" 是逐个替换,结果是 "a b"(三个空格),符合“一一对应替换”语义。
- 需要保留空白数量 → 用单字符正则
"\p{Z}|\s" - 需要压缩多余空白 → 改用
"\s+"或更严格的"[\p{Z}\s]+" -
"\s+"在某些 JDK 版本中对 Unicode 分隔符支持不稳定,\p{Z}更明确
容易忽略的边界情况和兼容性问题
以下字符常被漏掉,但实际属于空白符:
- Unicode 不间断空格
u00A0(\s在 JDK 8 默认不匹配,\p{Z}可覆盖) - 中文全角空格
u3000(属于Zs,\p{Z}覆盖) - 零宽空格
u200B(属于Cf类,\p{Z}不覆盖;如需处理,得额外加\p{Cf}) - 行结束符
u2028(LINE SEPARATOR)、u2029(PARAGRAPH SEPARATOR)→ 属于Zl和Zp,\p{Z}已包含
如果业务明确要求处理零宽类控制符,正则应扩展为:"\p{Z}|\s|\p{Cf}",但多数场景不需要——它不属于“空白符”的常规理解范畴。
性能与实际调用注意事项
replaceAll() 每次都编译正则,高频调用时建议预编译 Pattern:
private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\p{Z}|\s");
// 使用时:
String result = WHITESPACE_PATTERN.matcher(str).replaceAll(" ");
另外注意:
- 输入为
null会抛NullPointerException,需提前判空 -
replaceAll()返回新字符串,原字符串不可变 - Android API 26+ 对
\p{Z}支持完整;旧版本可能需降级为"\s|\u00A0|\u3000|\u2000-\u200B|\u2028|\u2029"手动枚举
真正麻烦的不是写法,而是确认你的数据里到底混了哪些“看不见的空白”——先用 str.codePoints().mapToObj(Integer::toHexString).collect(Collectors.toList()) 打印码点,再决定正则要不要加例外。










