Почему Javascript Regex подходит каждый раз?

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

Я пробовал этот код в Opera и Firefox. В обоих случаях поведение одинаковое:

>>> domainRegex = /(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/g;
/(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/g 
>>> domainRegex.exec('mail-we0-f174.google.com');
Array [".google.com", "google.com"]
>>> domainRegex.exec('mail-we0-f174.google.com');
null
>>> domainRegex.exec('mail-we0-f174.google.com');
Array [".google.com", "google.com"]
>>> domainRegex.exec('mail-we0-f174.google.com');
null
>>> domainRegex.exec('mail-we0-f174.google.com');
Array [".google.com", "google.com"]
>>> domainRegex.exec('mail-we0-f174.google.com');
null

Почему это происходит? Описан ли это поведение? Есть ли способ вокруг, кроме определения регулярного выражения внутри тела цикла?

Ответы

Ответ 1

exec() работает так, как вы описали; с присутствующим модификатором /g, он вернет совпадение, начиная с lastIndex с каждым вызовом, пока не будет больше совпадений, при котором point возвращает null, а значение lastIndex равно reset до 0.

Однако, поскольку вы привязали выражение с помощью $, будет не более одного совпадения, поэтому вы можете использовать String.match() вместо этого и потеряет модификатор /g:

var domainRegex = /(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/;
'mail-we0-f174.google.com'.match(domainRegex); // [".google.com", "google.com"]

Ответ 2

Каждый раз, когда вы запускаете метод exec своего регулярного выражения, он получает следующее совпадение.

Как только он достигнет конца строки, он возвращает null, чтобы вы знали, что у вас есть все совпадения. В следующий раз он начнется с самого начала.

Поскольку у вас есть только одно совпадение (которое возвращает массив полного соответствия и совпадение из скобок), в первый раз регулярное выражение начинает поиск с самого начала. Он находит совпадение и возвращает его. В следующий раз он дойдет до конца и вернет null. Так что, если бы у вас было это в цикле, вы могли бы сделать что-то вроде этого, чтобы пройти все совпадения:

while(regExpression.exec(string)){
    // do something
}

Затем в следующий раз он снова начинается с позиции 0.

"Есть ли способ?"

Хорошо, если вы знаете только одно совпадение или хотите только первое совпадение, вы можете сохранить результат te переменной. Нет необходимости повторно использовать .exec. Если вас интересуют все совпадения, вам нужно продолжать, пока не получите null.

Ответ 3

При выполнении глобального поиска с RegExp метод exec начинает сопоставлять начало с свойство lastIndex. Свойство lastIndex устанавливается при каждом вызове exec и устанавливается в положение после последнего найденного совпадения. Если совпадение не выполняется, lastIndex имеет значение reset до 0, что приводит к тому, что exec будет совпадать с началом.

var a = 'asdfeeeasdfeedxasdf'
undefined
var p = /asdf/g
p.lastIndex
4
p.exec(a)
["asdf"]
p.lastIndex
11
p.exec(a)
["asdf"]
p.lastIndex
19
p.exec(a)
null //match failed
p.lastIndex
0 //lastIndex reset. next match will start at the beginning of the string a

p.exec(a)
["asdf"]

Ответ 4

почему вы не используете простой метод сопоставления для строки типа

'mail-we0-f174.google.com'.match(/(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/)

Ответ 5

Дополнительная информация в Ответ Ja͢cks:

Вы также можете установить lastIndex

var myRgx = /test/g;
myRgx.exec(someString);
myRgx.lastIndex = 0;

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

new RegExp(myRgx).exec(someString);