Ответ 1
Копаясь в исходном коде, я получил точную проблему за этим поведением.
Метод String.split()
внутренне использует Pattern.split()
. Метод 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
Если последнее условие в приведенном выше коде - index == 0
, истинно, тогда массив с одним элементом возвращается со строкой ввода.
Теперь рассмотрим случаи, когда index
может быть 0
.
- Если совпадения нет. (Как уже в комментарии выше этого условия)
-
Если совпадение найдено в начале, а длина строки соответствует
0
, тогда значение индекса в блокеif
(внутри циклаwhile
) -index = m.end();
будет равно 0. Единственная возможная строка соответствия - пустая строка (length = 0). Это именно так. А также не должно быть никаких дополнительных совпадений, иначе
index
будет обновлен до другого индекса.
Итак, учитывая ваши случаи:
-
Для
d%
перед шаблоном существует только одно совпадение перед первымd
. Следовательно, значение индекса будет0
. Но поскольку дальнейших совпадений нет, значение индекса не обновляется, а условиеif
становитсяtrue
и возвращает массив одиночных элементов с исходной строкой. -
Для
d20+2
должно быть два совпадения: один передd
и один до+
. Таким образом, значение индекса будет обновляться, и, следовательно, будет возвращенArrayList
в приведенном выше коде, который содержит пустую строку в результате разделения на разделитель, который является первым символом строки, как уже объяснялось в ответе @Stema.
Итак, чтобы получить нужное поведение (которое делится на разделитель только тогда, когда оно не в начале, вы можете добавить отрицательный внешний вид в свой шаблон регулярного выражения):
"(?<!^)(?=[dk+-])" // You don't need to escape + and hyphen(when at the end)
это разделится на пустую строку, за которой следует ваш класс символов, но не предшествует началу строки.
Рассмотрим случай разделения строки "ad%"
на шаблон регулярного выражения - "a(?=[dk+-])"
. Это даст вам массив с первым элементом как пустую строку. Единственное изменение здесь: пустая строка заменяется на a
:
"ad%".split("a(?=[dk+-])"); // Prints - `[, d%]`
Почему? Это потому, что длина строки соответствует 1
. Таким образом, значение индекса после первого совпадения - m.end()
не будет 0
, а 1
, и, следовательно, массив элементов не будет возвращен.