Почему RegExp с глобальным флагом дает неправильные результаты? - PullRequest
241 голосов
/ 05 октября 2009

В чем проблема с этим регулярным выражением, когда я использую глобальный флаг и флаг без учета регистра? Запрос - это пользовательский ввод. Результат должен быть [true, true].

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));
result.push(re.test('Foo Bar'));
// result will be [true, false]

var reg = /^a$/g;
for(i = 0; i++ < 10;)
   console.log(reg.test("a"));

Ответы [ 6 ]

304 голосов
/ 05 октября 2009

Объект RegExp отслеживает lastIndex, где произошло совпадение, поэтому при последующих совпадениях он будет начинаться с последнего использованного индекса, а не с 0. Посмотрите:

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));

alert(re.lastIndex);

result.push(re.test('Foo Bar'));

Если вы не хотите вручную сбрасывать lastIndex на 0 после каждого теста, просто удалите флаг g.

Вот алгоритм, который диктуют спецификации (раздел 15.10.6.2): ​​

RegExp.prototype.exec (строка)

Проводит совпадение регулярного выражения строки против регулярного выражения и возвращает объект Array, содержащий результаты матча или ноль, если Строка не соответствует Строка ToString (строка) ищется для появление регулярного выражения шаблон следующим образом:

  1. Пусть S будет значением ToString (string).
  2. Пусть длина будет длиной S.
  3. Пусть lastIndex будет значением свойства lastIndex.
  4. Позвольте мне быть значением ToInteger (lastIndex).
  5. Если глобальное свойство имеет значение false, пусть i = 0.
  6. Если длина I <0 или I>, тогда установите lastIndex в 0 и верните ноль.
  7. Вызвать [[Match]], передав ему аргументы S и i. Если [[Match]] возвращенная ошибка, перейдите к шагу 8; иначе пусть r будет результатом его состояния и перейдите к шагу 10.
  8. Пусть i = i + 1.
  9. Перейти к шагу 6. ​​
  10. Пусть e будет значением endIndex для r.
  11. Если глобальное свойство имеет значение true, установите для lastIndex значение e.
  12. Пусть n будет длиной массива захватов r. (Это тоже самое значение как 15.10.2.1 NCapturingParens.)
  13. Возвращает новый массив со следующими свойствами:
    • Указатель свойство устанавливается в положение совпадающая подстрока в полной строка S.
    • свойство ввода установлено к с.
    • Для свойства length установлено значение n + 1.
    • Для свойства 0 установлено значение совпадающая подстрока (то есть часть S между смещением I включительно и смещение е эксклюзив).
    • Для каждого целое число i такое, что I> 0 и I ≤ n, установите свойство с именем ToString (i) в i-й элемент массива захватов r.
65 голосов
/ 05 октября 2009

Вы используете один объект RegExp и выполняете его несколько раз. При каждом последующем выполнении он продолжается с последнего индекса соответствия.

Вам необходимо «переустанавливать» регулярное выражение для начала с начала перед каждым выполнением:

result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
// result is now [true, true]

Сказав, что может быть более удобочитаемым создание нового объекта RegExp каждый раз (издержки минимальны, поскольку RegExp все равно кэшируется):

result.push((/Foo B/gi).test(stringA));
result.push((/Foo B/gi).test(stringB));
34 голосов
/ 05 октября 2009

RegExp.prototype.test обновляет свойство lastIndex регулярных выражений, чтобы каждый тест начинался там, где остановился последний. Я бы предложил использовать String.prototype.match, поскольку он не обновляет свойство lastIndex:

!!'Foo Bar'.match(re); // -> true
!!'Foo Bar'.match(re); // -> true

Примечание: !! преобразовывает его в логическое значение, а затем инвертирует логическое значение, чтобы оно отражало результат.

Кроме того, вы можете просто сбросить свойство lastIndex:

result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
9 голосов
/ 13 ноября 2013

Снятие глобального g флага решит вашу проблему.

var re = new RegExp(query, 'gi');

Должно быть

var re = new RegExp(query, 'i');
0 голосов
/ 29 июня 2018

У меня была функция:

function parseDevName(name) {
  var re = /^([^-]+)-([^-]+)-([^-]+)$/g;
  var match = re.exec(name);
  return match.slice(1,4);
}

var rv = parseDevName("BR-H-01");
rv = parseDevName("BR-H-01");

Первый звонок работает. Второй звонок нет. Операция slice жалуется на нулевое значение. Я предполагаю, что это из-за re.lastIndex. Это странно, потому что я ожидал, что новый RegExp будет назначаться каждый раз, когда вызывается функция, а не совместно использоваться несколькими вызовами моей функции.

Когда я изменил его на:

var re = new RegExp('^([^-]+)-([^-]+)-([^-]+)$', 'g');

Тогда я не получаю эффект удержания lastIndex. Это работает так, как я ожидал.

0 голосов
/ 21 сентября 2017

Использование флага / g указывает ему продолжить поиск после попадания.

Если совпадение выполнено успешно, метод exec () возвращает массив и обновляет свойства объекта регулярного выражения.

Перед первым поиском:

myRegex.lastIndex
//is 0

После первого поиска

myRegex.lastIndex
//is 8

Удаляет g и выходит из поиска после каждого вызова exec ().

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