Почему 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);