在Java 8之前,当我们分割空字符串时
String[] tokens = "abc".split("");
拆分机制将在标有的地方拆分 |
|a|b|c|
因为”“每个字符前后都有空白。因此,结果将首先生成此数组
["", "a", "b", "c", ""]
稍后将删除结尾的空字符串(因为我们没有为limit参数显式提供负值),因此它将最终返回
["", "a", "b", "c"]
在Java 8中,拆分机制似乎已更改。现在当我们使用
"abc".split("")
我们将得到["a", "b", "c"]
数组而不是数组,["", "a", "b", "c"]
因此看起来开始时的空字符串也被删除了。但是这个理论失败了,因为
"abc".split("a")
在开始时返回带有空字符串的数组["", "bc"]
。
有人可以解释一下这是怎么回事,以及拆分规则在Java 8中是如何变化的?
String.split
(调用Pattern.split
)的行为在Java 7和Java 8之间改变。
文献资料
Pattern.split
在Java 7和Java 8的文档之间进行比较,我们观察到添加了以下子句:
如果在输入序列的开头存在一个正宽匹配,则在结果数组的开头将包含一个空的前导子字符串。开头的零宽度匹配永远不会产生这样的空前导子字符串。
与Java 7相比String.split
,Java 8中还添加了相同的子句。
参考实施
让我们比较一下Pattern.split
Java 7和Java 8中的参考实现的代码。该代码是从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);
// 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);
}
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);
// 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;
}
保持兼容性
Java 8及更高版本中的以下行为
为了使make split
在各个版本中的行为保持一致并与Java 8中的行为兼容:
(?!\A)
在正则表达式的末尾添加,然后将原始正则表达式包装在非捕获组中即可(?:...)
(如有必要)。(?!\A)
检查字符串是否不以字符串开头结尾,这意味着匹配在字符串开头为空匹配。Java 7及更高版本中的以下行为
没有通用的解决方案可以使它split
与Java 7及更低版本向后兼容,除非替换所有实例split
以指向你自己的自定义实现。
在Java8之前,当我们在空字符串上拆分时 拆分机制将在标记有的地方拆分 因为每个字符前后都有空格”。因此,它首先会生成这个数组 稍后将删除尾随的空字符串(因为我们没有显式地为参数提供负值),所以它最终将返回 在Java8中,拆分机制似乎发生了变化。现在当我们使用 我们将获得数组,而不是。 我的第一个猜测是,可能现在前导的空字符串也会像尾随的空字符串一样被删除。 但这个理论失败了,因为 返回,因此
问题内容: 我有一个像这样的字符串: 现在,我想删除所有的换行符。(但是只有出现在第一个“实词”之前的那个。这怎么可能? 谢谢 问题答案: 如果可以从字符串的 两端 删除换行符(和其他空格),则可以使用 要仅删除 前导 换行符/空格字符,您可以(例如)使用正则表达式搜索并替换: 匹配字符串开头的所有空格。使用只匹配换行符。 Swift 3(Xcode 8) 更新 :
问题内容: path = “/Volumes/Users” >>> path.lstrip(‘/Volume’) ‘s/Users’ >>> path.lstrip(‘/Volumes’) ‘Users’ >>> 我期望的输出是 问题答案: 是基于字符的,它将删除该字符串中左端的所有字符。 要验证这一点,请尝试以下操作: 由于是字符串的一部分,因此将其删除。 您需要改用切片: 或者,在Python
问题内容: 如何在Java中从字符串开头删除空格而不从结尾删除空格? 如果值为: 然后,如何只删除前导空格,如下所示: 我当前的实现是遍历,检查第一个字符并创建一个子字符串,直到达到第一个非空白值。 谢谢! 问题答案: 您可以使用:
问题内容: 在空字符串上分割将返回大小为1的数组: 考虑这返回空数组: 请解释 :) 问题答案: 出于同样的原因 和 将返回一个大小为2的数组。将第一个匹配之前的所有内容作为第一个元素返回。
问题内容: 我有一个字符数组,一次最多可以容纳50000个字符。该数组的内容通过套接字连接来。但是,不能保证此字符缓冲区不会有任何空元素。然后,我需要将此字符数组转换为字符串(例如,新的String(buffer);)。我的问题是,每当我从套接字收到的缓冲区长度不超过50000时,如何从此char数组或String中删除剩余的元素或空元素?如果没有,您建议的最有效方法是什么? / 这是当前的实现