在Java8之前,当我们在空字符串上拆分时
String[] tokens = "abc".split("");
拆分机制将在标记有|
的地方拆分
|a|b|c|
因为每个字符前后都有空格“
”。因此,它首先会生成这个数组
["", "a", "b", "c", ""]
稍后将删除尾随的空字符串(因为我们没有显式地为limit
参数提供负值),所以它最终将返回
["", "a", "b", "c"]
在Java8中,拆分机制似乎发生了变化。现在当我们使用
"abc".split("")
我们将获得[“a”、“b”、“c”]
数组,而不是[,“a”、“b”、“c”]
。
我的第一个猜测是,可能现在前导的空字符串也会像尾随的空字符串一样被删除。
但这个理论失败了,因为
"abc".split("a")
返回["", "bc"]
,因此前导空字符串未被删除。
有人能解释一下这是怎么回事吗?在Java8中,split
的规则是如何改变的?
split()
的文档从Java 7到Java 8略有变化。具体而言,增加了以下声明:
如果此字符串的开头有正宽度匹配,则结果数组的开头将包含一个空的前导子字符串。然而,开头的零宽度匹配永远不会产生这样的空前导子字符串。
(我的重点)
空字符串拆分在开始时生成零宽度匹配,因此根据上面指定的内容,空字符串不包括在结果数组的开始处。相比之下,您的第二个例子在"a"
上拆分,在字符串的开始处生成正宽度匹配,因此空字符串实际上包括在结果数组的开始处。
这已在拆分(String regex,限制)
的留档中指定。
如果此字符串的开头有正宽度匹配,则结果数组的开头将包含一个空的前导子字符串。然而,开头的零宽度匹配永远不会产生这样的空前导子字符串。
在"abc"中。拆分(")
开头有一个零宽度匹配,因此导致的空子串不包括在结果数组中。
然而,在第二个代码片段中,当你在“a”
上拆分时,你得到了一个正的宽度匹配(在本例中为1),因此空的前导子字符串将按预期包含在内。
(删除了不相关的源代码)
字符串的行为。split
(它调用模式。split
)在Java7和Java8之间变化。
比较模式的文档。拆分
在Java 7和Java 8中,我们注意到添加了以下条款:
当输入序列的开头有正宽度匹配时,结果数组的开头会包含一个空的前导子字符串。然而,开头的零宽度匹配永远不会产生这样的空前导子字符串。
同样的子句也被添加到字符串中。与Java 7相比,在Java 8中拆分
。
让我们比较一下模式的代码。Java 7和Java 8中参考实现的拆分
。代码从grepcode中检索,适用于7u40-b43和8-b132版本。
public String[] split(CharSequence input, int limit) {
int index = 0;
boolean matchLimited = limit > 0;
ArrayList<String> matchList = new ArrayList<>();
Matcher m = matcher(input);
// Add segments before each match found
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) { // last one
String match = input.subSequence(index,
input.length()).toString();
matchList.add(match);
index = m.end();
}
}
// If no match was found, return this
if (index == 0)
return new String[] {input.toString()};
// Add remaining segment
if (!matchLimited || matchList.size() < limit)
matchList.add(input.subSequence(index, input.length()).toString());
// Construct result
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);
}
public String[] split(CharSequence input, int limit) {
int index = 0;
boolean matchLimited = limit > 0;
ArrayList<String> matchList = new ArrayList<>();
Matcher m = matcher(input);
// Add segments before each match found
while(m.find()) {
if (!matchLimited || matchList.size() < limit - 1) {
if (index == 0 && index == m.start() && m.start() == m.end()) {
// no empty leading substring included for zero-width match
// at the beginning of the input char sequence.
continue;
}
String match = input.subSequence(index, m.start()).toString();
matchList.add(match);
index = m.end();
} else if (matchList.size() == limit - 1) { // last one
String match = input.subSequence(index,
input.length()).toString();
matchList.add(match);
index = m.end();
}
}
// If no match was found, return this
if (index == 0)
return new String[] {input.toString()};
// Add remaining segment
if (!matchLimited || matchList.size() < limit)
matchList.add(input.subSequence(index, input.length()).toString());
// Construct result
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()) {
// no empty leading substring included for zero-width match
// at the beginning of the input char sequence.
continue;
}
若要使拆分
在版本间行为一致,并与Java8中的行为兼容:
(?!\A)
,并将原始正则表达式包装在非捕获组(?:…)
(如有必要)
如果正则表达式不能匹配零长度字符串,则无需执行任何操作
如果不知道正则表达式是否可以匹配零长度字符串,请执行步骤1中的两个操作
(?!\A)
检查字符串没有在字符串的开头结束,这意味着匹配是字符串开头的空匹配。
除了替换
split
的所有实例以指向您自己的自定义实现之外,没有通用的解决方案可以使split
与Java 7及更早版本向后兼容。
问题内容: 在Java 8之前,当我们分割空字符串时 拆分机制将在标有的地方拆分 | 因为”“每个字符前后都有空白。因此,结果将首先生成此数组 稍后将删除结尾的空字符串(因为我们没有为limit参数显式提供负值),因此它将最终返回 在Java 8中,拆分机制似乎已更改。现在当我们使用 我们将得到数组而不是数组,因此看起来开始时的空字符串也被删除了。但是这个理论失败了,因为 在开始时返回带有空字符串
问题内容: 在空字符串上分割将返回大小为1的数组: 考虑这返回空数组: 请解释 :) 问题答案: 出于同样的原因 和 将返回一个大小为2的数组。将第一个匹配之前的所有内容作为第一个元素返回。
问题内容: 我试图使用分隔符拆分值。但是我发现了令人惊讶的结果 我期望得到8个值。[5,6,7,EMPTY,8,9,EMPTY,EMPTY] 但是我只得到6个值。 任何想法以及如何解决。无论EMPTY值在何处,它都应该位于数组中。 问题答案: 默认情况下,从结果数组中删除结尾的空字符串。要关闭此机制,我们需要使用重载版本,将其设置为负值,例如 更多细节: 内部返回该方法的结果,你可以在此方法的文档
任何想法和如何修复。无论空值出现在任何位置,它都应该在数组中。
问题内容: 我想将字符串拆分为单个字符。所以我做: 但这产生了: 在其他语言(例如Ruby)中执行相同操作时,我不习惯使用第一个空字符串。它背后的逻辑是什么? 问题答案: 你为什么要用这个?使用可能会更好。 我知道一个会返回一个字符串数组,而另一个会给您一个字符数组。由于您希望分别使用每个字符,因此我假设这与您的代码无关。
问题内容: path = “/Volumes/Users” >>> path.lstrip(‘/Volume’) ‘s/Users’ >>> path.lstrip(‘/Volumes’) ‘Users’ >>> 我期望的输出是 问题答案: 是基于字符的,它将删除该字符串中左端的所有字符。 要验证这一点,请尝试以下操作: 由于是字符串的一部分,因此将其删除。 您需要改用切片: 或者,在Python