Некоторый контекст
С Javascript: полное руководство :
Если regexp
является глобальным регулярным выражением, то exec()
ведет себя немного более сложным образом. Он начинает поиск string
с позиции символа, указанной в свойстве lastIndex
regexp
. Когда он находит совпадение, он устанавливает lastIndex
в положение первого символа после совпадения.
Думаю, любой, кто регулярно работает с javascript RegExps, узнает этот отрывок. Однако я обнаружил странное поведение в этом методе.
Проблема
Рассмотрим следующий код:
>> rx = /^(.*)$/mg
>> tx = 'foo\n\nbar'
>> rx.exec(tx)
[foo,foo]
>> rx.lastIndex
3
>> rx.exec(tx)
[,]
>> rx.lastIndex
4
>> rx.exec(tx)
[,]
>> rx.lastIndex
4
>> rx.exec(tx)
[,]
>> rx.lastIndex
4
RegExp, похоже, застревает во второй строке и не увеличивает свойство lastIndex
. Кажется, это противоречит Книге носорогов . Если я сам установлю его следующим образом, он продолжится и в конечном итоге вернет значение NULL, как и ожидалось, но, похоже, я не должен был это делать.
>> rx.lastIndex = 5
5
>> rx.exec(tx)
[bar,bar]
>> rx.lastIndex
8
>> rx.exec(tx)
null
Заключение
Очевидно, я могу увеличивать свойство lastIndex
каждый раз, когда совпадение является пустой строкой. Однако, будучи любознательным типом, я хочу знать, почему он не увеличивается методом exec
. Почему не так?
Примечания
Я наблюдал такое поведение в Chrome и Firefox. Кажется, это происходит только при наличии соседних строк новой строки.
[править]
Tomalak говорит ниже, что изменение шаблона на /^(.+)$/gm
приведет к тому, что выражение не застрянет, но пустая строка игнорируется. Можно ли это изменить, чтобы соответствовать строке? Спасибо за ответ Томалак !
[править]
Использование следующего шаблона и использование группы 1 работает для всех строк, которые я могу придумать. Еще раз спасибо Томалак .
/^(.*)((\r\n|\r|\n)|$)/gm
[править]
Предыдущий шаблон возвращает пустую строку. Однако, если вам не нужны пустые строки, Tomalak дает следующее решение, которое я считаю более чистым.
/^(.*)[\r\n]*/gm
[править]
Оба предыдущих решения застряли на завершающих символах новой строки, поэтому вам придется либо убрать их, либо увеличить lastIndex
вручную.
[править]
Я нашел отличную статью, подробно описывающую проблемы кросс-браузеров с lastIndex
в Flagrant Badassery . Помимо потрясающего названия блога, статья дала мне более глубокое понимание проблемы, а также хорошее кросс-браузерное решение. Решение заключается в следующем:
var rx = /^/gm,
tx = 'A\nB\nC',
m;
while(m = rx.exec(tx)){
if(!m[0].length && rx.lastIndex > m.index){
--rx.lastIndex;
}
foo();
if(!m[0].length){
++rx.lastIndex;
}
}