Неверный вывод из String.split

Я не понимаю вывод этого кода:

public class StringDemo{              
    public static void main(String args[]) {
        String blank = "";                    
        String comma = ",";                   
        System.out.println("Output1: "+blank.split(",").length);  
        System.out.println("Output2: "+comma.split(",").length);  
    }
}

И получил следующий вывод:

Output1: 1 
Output2: 0

Ответ 1

Документация:

Для: System.out.println("Output1: "+blank.split(",").length);

Массив, возвращаемый этим методом, содержит каждую подстроку этой строки, которая заканчивается другой подстрокой, которая соответствует заданному выражению или заканчивается в конце строки. Подстроки в массиве находятся в том порядке, в котором они встречаются в этой строке. Если выражение не соответствует какой-либо части ввода, то результирующий массив имеет только один элемент, а именно эту строку.

Он просто вернет всю строку, поэтому возвращает 1.


Во втором случае String.split отбрасывает ,, поэтому результат будет пустым.

String.split silently discards trailing separators

см. guava StringsExplained тоже

Ответ 2

Все происходит в соответствии с планом, но пусть делает это шаг за шагом (надеюсь, у вас есть время).

Согласно documentationисходный код) метода split(String regex):

Этот метод работает, как если бы он вызвал метод разделения с двумя аргументами с данным выражением и предельным аргументом нуля.

Итак, когда вы вызываете

split(String regex)

вы фактически получаете результат от метода split(String regex, int limit), который вызывается таким образом:

split(regex, 0)

Итак, здесь limit установлено значение 0.

Вам нужно знать несколько вещей об этом параметре:

  • Если limit положительно, вы ограничиваете длину массива результатов указанным вами положительным числом, поэтому "axaxaxaxa".split("x",2) вернет массив ["a", "axaxaxa"], а не ["a","a","a","a","a"].
  • Если limit - 0, то вы не ограничиваете длину массива результатов. Но это также означает, что любые конечные пустые строки будут удалены. Например:

    "fooXbarX".split("X")
    

    начнет генерировать массив, который будет выглядеть так:

    ["foo", "bar", ""]
    

    ("barX" split on "X" генерирует "bar" и ""), но поскольку split удаляет всю завершающую пустую строку, он вернет

    ["foo", "bar"]
    
  • Поведение отрицательного значения limit аналогично поведению, где предел установлен на 0 (он не будет ограничивать длину массива результатов). Единственное различие заключается в том, что он не будет удалять пустые строки из конца массива результатов. Другими словами

    "fooXbarX".split("X",-1)
    

вернет ["foo", "bar", ""]


Давайте рассмотрим случай,

",".split(",").length

который (как объяснялось ранее) аналогичен

",".split(",", 0).length

Это означает, что мы используем версию split, которая не будет ограничивать длину массива результатов, но удалит все конечные пустые строки, "". Вам нужно понять, что когда мы разделяем одно, мы всегда получаем две вещи.

Другими словами, если мы разделим "abc" вместо b, получим "a" и "c".
Трудная часть состоит в том, чтобы понять, что если мы разделим "abc" на c, мы получим "ab" и "" (пустая строка).

Используя эту логику, если мы разделим "," на ,, мы получим "" и "" (две пустые строки).

Вы можете проверить его с помощью split с отрицательным пределом:

for (String s: ",".split(",", -1)){
    System.out.println("\""+s+"\"");
}

который будет печатать

""
""

Итак, как мы видим, массив результатов здесь сначала ["", ""].

Но поскольку по умолчанию мы используем limit, установленный в 0, все конечные пустые строки будут удалены. В этом случае массив результатов содержит только завершающие пустые строки, поэтому все они будут удалены, оставив вам пустой массив [], длина которого 0.


Чтобы ответить на вопрос с помощью

"".split(",").length

вам нужно понять, что удаление конечных пустых строк имеет смысл только в том случае, если такие конечные пустые строки являются результатом разделения (и, скорее всего, не нужны).
Поэтому, если бы не было каких-либо мест, на которых мы могли бы разделиться, нет никаких шансов на создание пустых строк, поэтому нет смысла запускать этот процесс "очистки".

Эта информация упоминается в документации метода split(String regex, int limit), где вы можете прочитать:

Если выражение не соответствует какой-либо части ввода, то в результирующем массиве имеется только один элемент, а именно эта строка.

Вы также можете увидеть это поведение в исходный код этого метода (из Java 8):

 2316  public String [] split (String regex,   int limit) {
2317/* fastpath, если регулярное выражение является
2318 (1) one- char String, и этот символ не является одним из
2319 Мета-символы RegEx". $|() [{^? * +\\ "или
2320 (2) two- char String и первая char - обратная косая черта и
2321 вторая не является буквой ascii или буквой ascii.
2322 */
2323 charch = 0;
2324 if (((regex.value.length == 1 &&
2325 ". $|() [{^? * +\\". indexOf (ch = regex. charAt (0)) == -1) ||
2326 (regex. length() == 2 &
2327 regex. charAt (0 ) == '\\' &&
2328 (((ch = regex. charAt (1)) - '0') | (' 9' -ch)) < 0 &
2329 ((ch-'a ') | (' z'-ch)) <; 0 &
2330 ((ch-'A ') | (' Z'-ch)) <; 0)) &
2331 (ch < Character.MIN_HIGH_SURROGATE ||
2332 ch> Character.MAX_LOW_SURROGATE))
2333 {
2334 int off = 0;
2335 int next = 0;
2336 boolean limited = limit> 0;
2337 ArrayList < String> list = new ArrayList <>();
2338 while ((next = indexOf (ch, off))!= - 1) {
2339 if (! Limited || size() < limit - 1) {
2340. add (substring (off, next));
2341 off = next + 1;
2342} else {//last one
2343//assert (list.size() == limit - 1);
2344. add (substring (off, value.length));
off = значение .length;
2346 break;
2347}
2348}
2349//Если совпадение не найдено, верните это
2350 if (off == 0)
2351 return новый String [] { this};
2353//Добавить оставшийся сегмент
2354 if (! Limited || список size() < limit)
2355. add (substring (off, value.length));
2357//Построить результат
2358 int resultSize = list. size();
2359 if (limit == 0) {
2360 while (resultSize> 0 && list. get (resultSize - 1). length() == 0) {
2361 resultSize -;
2362}
2363}
2364 String [] result = new String [resultSize];
2365 return subList (0, resultSize). toArray (результат);
2366}
2367 return Патте rn. compile ( regex). split ( this, limit);
2368}
< бр /">

где вы можете найти

if (off == 0)
    return new String[]{this};

что означает

  • if (off == 0) - если off (позиция, из которой метод должен начинать поиск следующего возможного соответствия для regex, переданного как аргумент split), по-прежнему 0 после итерации по всей строке, мы не нашли соответствия, поэтому строка не была разделена
  • return new String[]{this}; - в этом случае просто верните массив с исходной строкой (представленной this).

Так как "," не удалось найти в "" даже один раз, "".split(",") должен возвращать массив с одним элементом (пустая строка, на которую вы вызывали split). Это означает, что длина этого массива 1.

BTW. Java 8 представила другой механизм. Он удаляет ведущие пустые строки (если они создаются в процессе расщепления), если мы разделяем с помощью регулярное выражение с нулевой длиной (например, "" или с внешним видом -оболочка (?<!x)). Дополнительная информация по адресу: Почему в Java 8 split иногда удаляет пустые строки в начале массива результатов?

Ответ 3

Из Документация по Java 1.7

Разделяет строку вокруг совпадений данного регулярного выражения.

Метод

split() работает так, как если бы он вызывал метод разделения с двумя аргументами с заданным выражением и предельный аргумент нуля. Таким образом, конечные пустые строки не включаются в результирующий массив.

В случае 1 blank.split(",") does not match any part of the input then the resulting array has just one element, namely this String.

It will return entire String. Итак, длина будет 1.

В случае 2 comma.split(",") will return empty.

split() ожидая регулярное выражение как аргумент, верните массив результатов в соответствие с этим регулярным выражением.

Итак, длина 0

Пример (Документация)

Строка "boo: и: foo" дает следующие результаты с этими выражениями:

Regex     Result
  :     { "boo", "and", "foo" }
  o     { "b", "", ":and:f" }

Параметры:   regex - регулярное выражение с разделителем

Возврат:   массив строк, вычисленный путем разбиения этой строки вокруг совпадений данного регулярного выражения

Броски:   PatternSyntaxException - если синтаксис регулярного выражения недействителен

Ответ 4

Из класса String javadoc для метода public String[] split(String regex):

Разделяет эту строку вокруг совпадений данного регулярного выражения.

Этот метод работает, как если бы он вызывал метод разделения с двумя аргументами с заданным выражением и предельным аргументом нулевого. Поэтому конечные пустые строки не включаются в результирующий массив.

В первом случае выражение не соответствует какой-либо части ввода, поэтому мы получили массив только с одним элементом - входом.

Во втором случае выражение соответствует вводу и split должно возвращать две пустые строки; но, согласно javadoc, они отбрасываются (потому что они отстают и пусты).

Ответ 5

Мы можем заглянуть в исходный код java.util.regex.Pattern, который находится за String.split. Путь вниз по кроличьей дыре методом

public String[] split(CharSequence input, int limit)

.

Вход ""

Для ввода "" этот метод называется

String[] parts = split("", 0);

Промежуточная часть этого метода::

  int index = 0;
  boolean matchLimited = limit > 0;
  ArrayList<String> matchList = new ArrayList<>();
  Matcher m = matcher(input);

  while(m.find()) {
    // Tichodroma: this will not happen for our input
  }

  // If no match was found, return this
  if (index == 0)
    return new String[] {input.toString()};

И вот что происходит: new String[] {input.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);

Здесь resultSize == 0 и limit == 0, поэтому new String[0] возвращается.

Ответ 6

Из JDK 1.7

 public String[] split(String regex, int limit) {
        /* fastpath if the regex is a
           (1)one-char String and this character is not one of the
              RegEx meta characters ".$|()[{^?*+\\", or
           (2)two-char String and the first char is the backslash and
              the second is not the ascii digit or ascii letter.
        */
        char ch = 0;
        if (((regex.count == 1 &&
             ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
             (regex.length() == 2 &&
              regex.charAt(0) == '\\' &&
              (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
              ((ch-'a')|('z'-ch)) < 0 &&
              ((ch-'A')|('Z'-ch)) < 0)) &&
            (ch < Character.MIN_HIGH_SURROGATE ||
             ch > Character.MAX_LOW_SURROGATE))
        {
            int off = 0;
            int next = 0;
            boolean limited = limit > 0;
            ArrayList<String> list = new ArrayList<>();
            while ((next = indexOf(ch, off)) != -1) {
                if (!limited || list.size() < limit - 1) {
                    list.add(substring(off, next));
                    off = next + 1;
                } else {    // last one
                    //assert (list.size() == limit - 1);
                    list.add(substring(off, count));
                    off = count;
                    break;
                }
            }
            // If no match was found, return this
            if (off == 0)
                return new String[] { this };

            // Add remaining segment
            if (!limited || list.size() < limit)
                list.add(substring(off, count));

            // Construct result
            int resultSize = list.size();
            if (limit == 0)
                while (resultSize > 0 && list.get(resultSize-1).length() == 0)
                    resultSize--;
            String[] result = new String[resultSize];
            return list.subList(0, resultSize).toArray(result);
        }
        return Pattern.compile(regex).split(this, limit);
    }

Итак, для этого случая регулярное выражение будет обрабатываться первым if.

Для первого случая blank.split(",")

// If no match was found, return this
if (off == 0)
   return new String[] { this };

Таким образом, эта функция вернет массив, который содержит один элемент, если нет согласованного.

Для второго случая comma.split(",")

List<String> list = new ArrayList<>();
//...
int resultSize = list.size();
if (limit == 0)
    while (resultSize > 0 && list.get(resultSize-1).length() == 0)
           resultSize--;
String[] result = new String[resultSize];
return list.subList(0, resultSize).toArray(result);

Как вы заметили, последний цикл while удалил весь пустой элемент в конце списка, поэтому resultSize - 0.

Ответ 7

String blank = "";                    
String comma = ",";                   
System.out.println("Output1: "+blank.split(",").length);  // case 1
System.out.println("Output2: "+comma.split(",").length);  // case 2

case 1 - Здесь blank.split(",") вернет "", так как в blank нет ,, вы получите то же самое, поэтому длина будет 1

case 2 Здесь comma.split(",") будет возвращать пустой массив, вам нужно scape ,, если вы хотите считать comma длиной 1 else длиной 0

Снова comma.split(",") split(), ожидающий аргумента regex в качестве аргумента, он вернет массив результатов, соответствующий этому regex.

Массив, возвращаемый этим методом, содержит каждую подстроку этого строка, которая заканчивается другой подстрокой, которая соответствует данному выражение или завершается в конце строки.

Else

Если выражение не соответствует какой-либо части ввода, тогда результирующий массив имеет только один элемент, а именно эту строку.

Ответ 8

API для метода split указывает, что "если выражение не соответствует какой-либо части ввода, то результирующий массив имеет только один элемент, а именно эту строку".

Итак, поскольку пустая строка не содержит ",", возвращается строка String [] с одним элементом (т.е. пустым).

Для строковой запятой из исходной строки остается "ничего", поэтому возвращается пустой массив.

Это кажется лучшим решением, если вы хотите обработать возвращенный результат, e. г.

String[] splits = aString.split(",");
for(String split: splits) {
   // do something
}