Подтвердить что ты не робот

Почему string.split с регулярным выражением, содержащим группу захвата, возвращает массив, который заканчивается пустой строкой?

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

Для этого я использую регулярное выражение /:(.+)/

Таким образом, строка

aaa:
bbb:ccc

Я ожидаю вывод

["aaa:\nbbb", "ccc"]

И учитывая строку

aaa:bbb:ccc

Я ожидаю вывод

["aaa", "bbb:ccc"]

Но когда я действительно запускаю эти команды, я получаю

["aaa:\nbbb", "ccc", ""]
["aaa", "bbb:ccc", ""]

Как вывод.

Итак, как-то javascript добавляет пустую строку в конец массива.

Я проверил документацию для String.split и в то же время упоминает, что если вы выполняете String.split в пустой строке с указанный разделитель, вы получите массив с 1 пустой строкой в ​​нем (а не пустой массив). Он не упоминает о том, что всегда всегда есть пустая строка на выходе или предупреждение о том, что вы можете получить этот результат, если вы совершаете распространенную ошибку или что-то в этом роде.

Я бы понял, была ли в моей строке ввода двоеточие в конце или что-то в этом роде; то он разбивается на двоеточие, а остальная часть матча - пустая строка. Это проблема, упомянутая в Разделение строки с регулярным выражением, чтобы сделать ее массивом без пустого элемента - но у меня нет этой проблемы, так как моя строка ввода не заканчивается моим сепаратор.

Я знаю, что быстрым решением в моем случае будет просто ограничение количества совпадений, через "aaa:bbb:ccc".split(/:(.+)/, 2), но мне все же интересно:

Почему этот вызов String.split возвращает массив, заканчивающийся пустой строкой?

4b9b3361

Ответ 1

Если мы изменим регулярное выражение на /:.+/ и выполним разделение на него, вы получите:

["aaa", ""]

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

>>> 'aaa:bbb:ccc'.split(':bbb:ccc')
['aaa', '']

Добавление группы захвата просто сохраняет bbb:ccc, но не должно изменять исходное поведение разложения.

Ответ 2

Интересно. Многому научился у этого вопроса. Позвольте мне поделиться тем, что я узнал.

Точка не соответствует новой строке

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

['aaa', '\nbbb:ccc', '']

правильно? Потому что .+ соответствует жадности. Таким образом, он должен быть разбит на :\nbbb:ccc, где : соответствует : и .+ соответствует \nbbb:ccc. Но фактический результат, который вы получили, был

[ 'aaa:\nbbb', 'ccc', '' ]

Это связано с тем, что . не соответствует терминаторам строк. Цитирование MDN,

(точка, десятичная точка) соответствует любому одиночному символу, кроме терминаторов строк:\n,\r,\u2028 или \u2029.

Итак, :\n не соответствует :(.+). Вот почему он не ломается. Если вы на самом деле должны совпадать с новой строкой,, используйте либо [^], либо [\s\S].

Например,

console.log(data.split(/:([\s\S]+)/));
// [ 'aaa:\nbbb', 'ccc', '' ]
console.log(data.split(/:([\s\S]+)/));
// [ 'aaa', '\nbbb:ccc', '' ]
console.log(data.split(/:([^]+)/));
// [ 'aaa', '\nbbb:ccc', '' ]

Теперь, чтобы ответить на ваш реальный вопрос, почему в конце разделения есть пустая строка. Когда вы сокращаете большую линию, сколько строк вы получаете? Две маленькие линии. Поэтому всякий раз, когда вы делаете разрез, должны быть два объекта. В вашем случае aaa:\nbbb - это первый разрез, фактическое место обрезания - :ccc, и поскольку строка заканчивается там, пустая строка включается, чтобы указать, что это конец строки.

Ответ 3

Из спецификация ECMAScript 2015 (String.prototype.split):

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

  "A<B>bold</B>and<CODE>coded</CODE>".split(/<(\/)?([^<>]+)>/)

вычисляет массив:

  ["A", undefined, "B", "bold", "/", "B", "and", undefined,
  "CODE", "coded", "/", "CODE", ""]

Как и в примере вашего примера, выходной массив здесь содержит завершающую пустую строку, которая является частью входной строки минус "coded", которая не захватывается шаблоном разделителя (который захватывает "/" и "CODE").

Не очевидно, но имеет смысл, поскольку в противном случае разделители будут заканчиваться в конце разделенного массива, где они фактически ничего не будут отделять.