通过挖掘源代码,我得到了这种行为背后的确切问题。
该方法在内部使用 。返回结果数组之前的 split 方法会检查最后一个匹配的索引,或者是否确实存在匹配项。如果最后匹配的索引是 ,则表示您的模式仅在字符串开头匹配一个空字符串或根本不匹配,在这种情况下,返回的数组是包含相同元素的单元素数组。String.split()
Pattern.split()
0
下面是源代码:
public String[] split(CharSequence input, int limit) {
int index = 0;
boolean matchLimited = limit > 0;
ArrayList<String> matchList = new ArrayList<String>();
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);
// Consider this assignment. For a single empty string match
// m.end() will be 0, and hence index will also be 0
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()};
// Rest of them is not required
如果上述代码中的最后一个条件 - 为 true,则返回单元素数组和输入字符串。index == 0
现在,考虑可以 .index
0
- 当根本没有匹配时。(如上面的评论中所述)
-
如果在开头找到匹配项,并且匹配字符串的长度为 ,则块中索引的值(循环内)-0
if
while
index = m.end();
将为 0。唯一可能的匹配字符串是空字符串(长度 = 0)。这里的情况正是如此。而且不应该有任何进一步的匹配,否则将更新为不同的索引。index
因此,考虑到您的情况:
对于 ,在第一个 之前,该模式只有一个匹配项。因此,索引值将为 。但是,由于没有任何进一步的匹配项,因此不会更新索引值,并且条件变为 ,并返回具有原始字符串的单元素数组。d%
d
0
if
true
因为将有两个匹配项,一个在 之前,一个在 之前。因此,索引值将被更新,因此将返回上述代码中的,其中包含空字符串,这是分隔符拆分的结果,分隔符是字符串的第一个字符,如@Stema的答案中已经解释的那样。d20+2
d
+
ArrayList
因此,要获得所需的行为(仅当不在开头时才在分隔符上进行拆分),您可以在正则表达式模式中添加一个否定的后看):
"(?<!^)(?=[dk+-])" // You don't need to escape + and hyphen(when at the end)
这将在空字符串后跟字符类上进行拆分,但不以字符串的开头开头进行拆分。
考虑在正则表达式模式上拆分字符串的情况 - 。这将为您提供一个数组,其中第一个元素为空字符串。这里唯一的变化是,空字符串被替换为:"ad%"
"a(?=[dk+-])"
a
"ad%".split("a(?=[dk+-])"); // Prints - `[, d%]`
为什么?这是因为匹配字符串的长度为 。因此,第一个匹配后的索引值 - 不会是 but ,因此不会返回单元素数组。1
m.end()
0
1