trim().split("\s+") 在输入为 null、纯空白串、含全角/零宽空格或混用 时会出错:trim 后得空串导致数组含 "",\s+ 不匹配全角空格(u3000)、不间断空格(u00a0)及零宽空格(u200b),且部分 jdk 对 识别不稳定。

为什么 String.trim().split("\\s+") 在某些情况下会出错
直接写 trim().split("\\s+") 看似合理,但实际运行时可能产生空字符串数组、索引越界或忽略连续换行符等意外结果。根本原因是 split() 对正则中量词的边界行为不敏感,且 trim() 只处理首尾空白,对中间的制表符、全角空格、零宽空格等无效。
split("\\s+") 的三个常见失效场景
以下情况会让切分结果不符合预期:
- 输入为
null或纯空白字符串(如" "),trim()后变成空串,split("\\s+")返回长度为1的数组,内容是空字符串"" - 文本含全角空格(
)、不间断空格(、U+00A0)或零宽空格(U+200B),\\s+完全匹配不到 - 多行文本中混用
和,部分 JDK 版本下\\s+对的识别不稳定
更健壮的替代写法(Java 8+)
推荐用两步:先用正则彻底清理所有空白类字符,再按非空白块提取。避免依赖 trim() 和 split() 的组合脆弱性:
String[] tokens = str == null ? new String[0] :
str.replaceAll("[\s\u00A0\u2000-\u200F\u2028\u2029\u3000]+", " ")
.trim()
.split(" ");
说明:
-
replaceAll()一次性覆盖 ASCII 空白、不换行空格、各类 Unicode 分隔符和中文全角空格 - 统一替换成单个 ASCII 空格,再
trim(),最后用字面量" "切分 —— 避免正则解析开销与歧义 - 如果需保留原始分隔符语义(比如要区分空格和制表符),改用
Pattern.compile("[^\s\u00A0\u2000-\u200F\u2028\u2029\u3000]+").matcher(str).results().map(MatchResult::group).toArray(String[]::new)
性能与兼容性提醒
多数业务场景下,上面的 replaceAll + split(" ") 组合比反复调用 split("\\s+") 更稳定;但若文本超长(>1MB)或高频调用(>1k/s),应预编译正则:
private static final Pattern WHITESPACE_NORMALIZER =
Pattern.compile("[\s\u00A0\u2000-\u200F\u2028\u2029\u3000]+");
另外注意:String.split() 在空字符串输入时返回 [""],不是 [],这个行为常被忽略,导致 tokens[0] NPE。










