До 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?
s.split("(?!^)")
вроде работает.split("")
вместо загадочная (для людей , которые не используют регулярные выражения)split("(?!^)")
илиsplit("(?<!^)")
или несколько других регулярных выражений.Ответы:
Поведение
String.split
(вызывающегоPattern.split
) меняется между Java 7 и Java 8.Документация
Сравнение между документацией
Pattern.split
в Java 7 и Java 8 , мы наблюдаем следующее предложение добавляется:Этот же пункт добавлен
String.split
в Java 8 по сравнению с Java 7 .Эталонная реализация
Давайте сравним код
Pattern.split
эталонной реализации в Java 7 и Java 8. Код получен из grepcode для версий 7u40-b43 и 8-b132.Java 7
Java 8
Добавление следующего кода в Java 8 исключает совпадение нулевой длины в начале входной строки, что объясняет поведение выше.
Поддержание совместимости
Следуя поведению в Java 8 и выше
Чтобы make
split
вел себя согласованно в разных версиях и был совместим с поведением в Java 8:(?!\A)
в конец регулярного выражения и оберните исходное регулярное выражение в группу без захвата(?:...)
(при необходимости).(?!\A)
проверяет, не заканчивается ли строка в начале строки, что означает, что совпадение является пустым совпадением в начале строки.Следуя поведению в Java 7 и ранее
Не существует общего решения для обеспечения
split
обратной совместимости с Java 7 и более ранними версиями, за исключением замены всех экземпляров,split
указывающих на вашу собственную реализацию.источник
split("")
код, чтобы он согласовывался между разными версиями Java?(?!^)
к концу регулярного выражения и укрыть оригинальные регулярные выражения , не захват группы(?:...)
(при необходимости), но я не могу вспомнить ни одного способ сделать его обратно совместимым (следуйте старому поведению в Java 7 и ранее)."(?!^)"
? В каких сценариях это будет отличаться""
? (Я ужасен в регулярных выражениях!: - /).Pattern.MULTILINE
флага, но\A
всегда соответствует началу строки независимо от флагов.Это указано в документации
split(String regex, limit)
.В
"abc".split("")
начале у вас есть совпадение нулевой ширины, поэтому ведущая пустая подстрока не включается в результирующий массив.Однако во втором фрагменте, когда вы разделите,
"a"
вы получили положительное совпадение ширины (в данном случае 1), поэтому пустая ведущая подстрока включена, как и ожидалось.(Удален нерелевантный исходный код)
источник
В документации для
split()
Java 7 было небольшое изменение на Java 8. В частности, был добавлен следующий оператор:(курсив мой)
Разделение пустой строки создает совпадение нулевой ширины в начале, поэтому пустая строка не включается в начало результирующего массива в соответствии с тем, что указано выше. Напротив, ваш второй пример, который разбивается,
"a"
генерирует совпадение с положительной шириной в начале строки, поэтому пустая строка фактически включается в начало результирующего массива.источник
"some-string".split("")
- это довольно редкий случай..split("")
это не единственный способ разделить ничего не сопоставив. Мы использовали регулярное выражение положительного просмотра вперед, которое в jdk7 также соответствовало в начале и создавало пустой элемент заголовка, которого теперь нет. github.com/spray/spray/commit/…