Почему 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
возвращает массив, заканчивающийся пустой строкой?
Ответы
Ответ 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"
).
Не очевидно, но имеет смысл, поскольку в противном случае разделители будут заканчиваться в конце разделенного массива, где они фактически ничего не будут отделять.