String.split
(调用Pattern.split
)的行为在Java 7和Java 8之间发生了变化。
文档
比较Java 7和Java 8中Pattern.split
的文档,我们可以看到添加了以下条款:
当输入序列开头有正宽度匹配时,结果数组的开头包括一个空的前导子字符串。然而,在开头处零宽度匹配从不产生这样的空前导子字符串。
与Java 7相比,Java 8中String.split
也添加了相同的条款。
参考实现
让我们比较Java 7和Java 8中的参考实现Pattern.split
代码。该代码是从grepcode检索的,版本为7u40-b43和8-b132。
Java 7
public String[] split(CharSequence input, int limit) {
int index = 0;
boolean matchLimited = limit > 0;
ArrayList<String> matchList = new ArrayList<>();
Matcher m = matcher(input);
while(m.find()) {
if (!matchLimited || matchList.size() < limit - 1) {
String match = input.subSequence(index, m.start()).toString();
matchList.add(match);
index = m.end();
} else if (matchList.size() == limit - 1) {
String match = input.subSequence(index,
input.length()).toString();
matchList.add(match);
index = m.end();
}
}
if (index == 0)
return new String[] {input.toString()};
if (!matchLimited || matchList.size() < limit)
matchList.add(input.subSequence(index, input.length()).toString());
int resultSize = matchList.size();
if (limit == 0)
while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
resultSize--;
String[] result = new String[resultSize];
return matchList.subList(0, resultSize).toArray(result);
}
Java 8
public String[] split(CharSequence input, int limit) {
int index = 0;
boolean matchLimited = limit > 0;
ArrayList<String> matchList = new ArrayList<>();
Matcher m = matcher(input);
while(m.find()) {
if (!matchLimited || matchList.size() < limit - 1) {
if (index == 0 && index == m.start() && m.start() == m.end()) {
continue;
}
String match = input.subSequence(index, m.start()).toString();
matchList.add(match);
index = m.end();
} else if (matchList.size() == limit - 1) {
String match = input.subSequence(index,
input.length()).toString();
matchList.add(match);
index = m.end();
}
}
if (index == 0)
return new String[] {input.toString()};
if (!matchLimited || matchList.size() < limit)
matchList.add(input.subSequence(index, input.length()).toString());
int resultSize = matchList.size();
if (limit == 0)
while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
resultSize--;
String[] result = new String[resultSize];
return matchList.subList(0, resultSize).toArray(result);
}
在Java 8中添加以下代码会排除输入字符串开头的零长度匹配项,这解释了上面的行为。
if (index == 0 && index == m.start() && m.start() == m.end()) {
continue;
}
保持兼容性
遵循Java 8及以上版本的行为
为了使split
在各个版本中表现一致并与Java 8中的行为兼容:
- 如果您的正则表达式可以匹配零长度字符串,请在正则表达式的末尾添加
(?!\A)
,并将原始正则表达式包装在非捕获组(?:...)
中(如果需要)。
- 如果您的正则表达式不能匹配零长度字符串,则不需要做任何事情。
- 如果您不知道正则表达式是否可以匹配零长度字符串,请同时执行步骤1中的两个操作。
(?!\A)
检查字符串不以字符串开头结束,这意味着匹配是一个空匹配在字符串开头。
遵循Java 7及以下版本的行为
除了将所有split
实例替换为指向自己的自定义实现之外,没有通用解决方案可以使split
向后兼容Java 7及以下版本。
split("")
而不是对于不使用正则表达式的人来说比较难懂的split("(?!^)")
或者split("(?<!^)")
,还有其他几个正则表达式。 - Pshemosplit("")
,请使用split("(?!^)")
- 它将尝试在文本开头之外的每个空字符串上进行拆分。顺便说一句,在 Java 8 中引入的正则表达式引擎中的另一个更改是\R
,它表示\n
、\r
或\r\n
(以及几个其他分隔符)。 - Pshemo