Почему «разбиение» на пустую строку возвращает непустой массив?


111

Разделение на пустую строку возвращает массив размером 1:

scala> "".split(',')
res1: Array[String] = Array("")

Учтите, что это возвращает пустой массив:

scala> ",,,,".split(',')
res2: Array[String] = Array()

Пожалуйста, объясни :)


5
Кроме того, это кажется несовместимым с поведением, наблюдаемым, когда строка содержит только один экземпляр разделителя. В этом случае результатом фактически является пустой массив: ",". Split (","). Length == 0
LD.

Ответы:


37

По той же причине, что

",test" split ','

и

",test," split ','

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


5
Пустая строка - это строка, а не ничего. (где угодно, только не в Excel)
Рафаэль

5
@Raphael Or в базе данных Oracle
Остин

7
@Raphael, на любом другом языке программирования "".split("wtf").lengthвозвращает 0. Только в JS это 1.: /
Андрей Михайлов - lolmaus

11
@ DanielC.Sobral Хорошо, так почему же "," split ","возвращает массив 0?
Джоан

5
Почему после последнего матча тоже не все возвращается?
Дидье А.

72

Если вы разделите апельсин ноль раз, у вас будет ровно один кусок - апельсин.


8
Но апельсин не пустой (если это означало олуиес), это апельсин. Возможно разделение апельсина, который должен быть там, но его нет, поэтому вы получите одно значение: пустое пространство xD
Ник Роландо

8
Это глубокий разговор.

31
Эта метафора имеет смысл "orange".split(','), но, очевидно, не имеет отношения к разделению пустых строк. Если я разделю недостаток апельсина ноль раз, у меня все равно не будет апельсина; представляем ли мы это как пустой список без апельсинов, список из ровно одного без апельсина, список из двенадцати без апельсинов или что? Вопрос не в том, что у нас получается, а в том, как мы это представляем.
Matchu

1
Но если разделить несуществующую книгу по страницам, ничего не получится.
SMUsamaShah 05

49

Методы разделения Java и Scala работают в два этапа, например:

  • Сначала разделите строку по разделителю. Естественным следствием является то, что, если строка не содержит разделителя, возвращается одноэлементный массив, содержащий только входную строку,
  • Во-вторых, удалите все крайние правые пустые строки. По этой причине ",,,".split(",")возвращается пустой массив.

Согласно этому результату "".split(",")должен быть пустой массив из-за второго шага, верно?

Должно. К сожалению, это искусственно созданный угловой случай. И это плохо, но , по крайней мере , это документировано в java.util.regex.Pattern, если вы помните , чтобы взглянуть на документацию:

Для n == 0 результат такой же, как для n <0, за исключением того, что завершающие пустые строки не будут возвращены. (Обратите внимание, что случай, когда ввод сам по себе является пустой строкой, является особым, как описано выше, и параметр limit к нему не применяется.)

Решение 1. Всегда передавайте -1 в качестве второго параметра

Итак, я советую вам всегда передавать n == -1в качестве второго параметра (это пропустит шаг два выше), если вы не знаете конкретно, чего хотите достичь / вы уверены, что пустая строка не является тем, что ваша программа получит в качестве входных данных.

Решение 2. Используйте класс Guava Splitter

Если вы уже используете Guava в своем проекте, вы можете попробовать класс Splitter (документация) . Он имеет очень богатый API и упрощает понимание вашего кода.

Splitter.on(".").split(".a.b.c.") // "", "a", "b", "c", ""
Splitter.on(",").omitEmptyStrings().split("a,,b,,c") // "a", "b", "c"
Splitter.on(CharMatcher.anyOf(",.")).split("a,b.c") // "a", "b", "c"
Splitter.onPattern("=>?").split("a=b=>c") // "a", "b", "c"
Splitter.on(",").limit(2).split("a,b,c") // "a", "b,c"

1
+1, это единственный ответ, который на самом деле цитирует документацию и указывает на ее непоследовательность. Однако я не нашел выделенную часть комментария в моем JavaDoc.
Йогу

Я нашел его в java.util.regex.Pattern, но, похоже, его больше нет. На момент написания он определенно присутствовал в официальном дереве исходных текстов OpenJDK как javadoc. android.googlesource.com/platform/libcore/+/… Может нам стоит сообщить об ошибке?
Рок Краль

Было бы неплохо сообщить об ошибке - поведение определенно не изменится, но, по крайней мере, оно должно быть задокументировано.
Йогу

@RokKralj Android не использовал библиотеку OpenJDK, а был основан на Apache Harmony, так что, может быть, вы ищете не в том месте?
lxgr

1
"".split (",", n)генерирует одноэлементный массив для n в (-1, 0, 1) с Oracle JDK 8. Было бы неплохо получить список только непустых токенов - думаю, может потребоваться полное регулярное выражение (что-то вроде "[^,\\s]+[^,]*[^,\\s]*").
simon.watts

40

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


2
Неправильно. Split удаляет все крайние правые пустые строки, поэтому результатом должен быть пустой массив. Смотрите мой ответ. ",".split(",")возвращает пустой массив.
Рок Краль

23

"a".split(",")-> "a" поэтому "".split(",")->""


6
Неправильно. Split удаляет все крайние правые пустые строки, поэтому результатом должен быть пустой массив. Смотрите мой ответ. ",".split(",")возвращает пустой массив.
Рок Краль

5

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


Я думаю, что это функция библиотеки, а не часть языка. Например, в Google Guava вы можете опустить пустые строки. > Итерируемый <String> штук = com.google.common.base.Splitter.on (','). OmitEmptyStrings (). Split ("");
oluies

2

Это splitповедение унаследовано от Java, хорошо это или
плохо ... Scala не отменяет определение из Stringпримитива.

Обратите внимание, что вы можете использовать limitаргумент для изменения поведения :

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

то есть вы можете установить, limit=-1чтобы получить поведение (всех?) других языков:

@ ",a,,b,,".split(",")
res1: Array[String] = Array("", "a", "", "b")

@ ",a,,b,,".split(",", -1)  // limit=-1
res2: Array[String] = Array("", "a", "", "b", "", "")

Кажется, хорошо известно, что поведение Java довольно запутано, но:

Вышеупомянутое поведение можно наблюдать, по крайней мере, от Java 5 до Java 8.

Была попытка изменить поведение, чтобы возвращать пустой массив при разделении пустой строки в JDK-6559590 . Однако вскоре он был возвращен в JDK-8028321, когда он вызывал регресс в различных местах. Это изменение никогда не попадает в первоначальную версию Java 8.

Примечание: метода разделения не было в Java с самого начала (его нет в 1.0.2 ), но фактически он присутствует как минимум в 1.4 (например, см. JSR51 около 2002 года). Я все еще расследую ...

Что неясно, так это то, почему Java выбрала это в первую очередь (я подозреваю, что изначально это была недосмотр / ошибка в «крайнем случае»), но теперь безвозвратно встроена в язык и поэтому остается .


Я не уверен, что это отвечает на вопрос - хотя это может быть правдой для приведенного здесь примера, это не помогает в случае пустой строки - по- "".split(",")прежнему возвращает массив с одним элементом, например [""].
DaveyDaveDave

@DaveyDaveDave, это ожидаемое поведение любого другого языка. «,,,,» - это странное / отличное поведение в Scala, несопоставимое со случаем «».
Энди Хайден

0

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

Some(str)
  .filter(_ != "")
  .map(_.split(","))
  .getOrElse(Array())
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.