RegExp.exec () время от времени возвращает NULL

87

Я серьезно схожу с ума по этому поводу, и я уже потратил непропорционально много времени, пытаясь понять, что здесь происходит. Так что дайте мне руку, пожалуйста =)

Мне нужно выполнить сопоставление строк в JavaScript с помощью RegExp. К сожалению, ведет себя очень странно. Этот код:

var rx = /(cat|dog)/gi;
var w = new Array("I have a cat and a dog too.", "There once was a dog and a cat.", "I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.");

for (var i in w) {
    var m = null;
    m = rx.exec(w[i]);
    if(m){
        document.writeln("<pre>" + i + "\nINPUT: " + w[i] + "\nMATCHES: " + m.slice(1) + "</pre>");
    }else{
        document.writeln("<pre>" + i + "\n'" + w[i] + "' FAILED.</pre>");
    }
}

Возвращает «кошка» и «собака» для первых двух элементов, как и должно быть, но затем некоторые exec()-calls начинают возвращаться null. Не понимаю почему.

Я отправил скрипку здесь , где вы можете запускать и редактировать код.

И до сих пор я пробовал это в Chrome и Firefox.

Ура!

/ Кристофер

cpak
источник
он терпит неудачу только на "I have a cat and a dog too.", кажется,
SilentGhost
exec возвращает null, если совпадение не удается по дизайну, поэтому по какой-то причине оно не соответствует.
Мартин Есперсен

Ответы:

84

О, вот оно. Поскольку вы определяете свое глобальное регулярное выражение, оно совпадает сначала catи на втором проходе цикла dog. Итак, в основном вам просто нужно сбросить ваше регулярное выражение (это внутренний указатель). Ср. это:

var w = new Array("I have a cat and a dog too.", "I have a cat and a dog too.", "I have a cat and a dog too.", "I have a cat and a dog too.");

for (var i in w) {
    var rx = /(cat|dog)/gi;
    var m = null;
    m = rx.exec(w[i]);
    if(m){
        document.writeln("<p>" + i + "<br/>INPUT: " + w[i] + "<br/>MATCHES: " + w[i].length + "</p>");
    }else{
        document.writeln("<p><b>" + i + "<br/>'" + w[i] + "' FAILED.</b><br/>" + w[i].length + "</p>");
    }
    document.writeln(m);
}
Тихий призрак
источник
вот и все, я был слишком медленным :)
Мартин Есперсен
ах мило! мне потребовалось бы время, чтобы понять это. благодаря!
cpak
Это сэкономило мне кучу времени. Спасибо!
Thomas Johansen
Эта проблема заставляет меня сомневаться в жизни.
GZ Xue
Я чувствую, что должен просто вернуть свою зарплату
cgatian
74

У объекта регулярного выражения есть свойство, lastIndexкоторое обновляется при запуске exec. Поэтому, когда вы выполняете регулярное выражение, например, «У меня есть кошка и собака тоже.», lastIndexУстанавливается значение 12. В следующий раз, когда вы запускаете execтот же объект регулярного выражения, он начинает поиск с индекса 12. Поэтому вам нужно сбросить lastIndexсвойство между каждым запуском.

Frode
источник
Ба, этот сайт слишком быстрый для меня. +1 для SilentGhost :-)
Frode
10
Спасибо за объяснение! Очень помогает настройка myRe.lastIndex = 0;для последующего использования.
Antony
1
Вау, большое спасибо за подсказку с lastIndex, это действительно сводило меня с ума!
dave0688 05
1
Я думаю, что это должен быть правильный ответ, потому что он следует передовой практике повторного использования одного и того же объекта регулярного выражения
smurtagh
Согласитесь, это должен быть правильный ответ. Он повторно использует один и тот же объект регулярного выражения, а также объясняет внутреннюю механику. OP следует рассмотреть возможность изменения.
Шон Коли,
34

Две вещи:

  1. Упомянутая необходимость сброса при использовании g(глобального) флага. Чтобы решить эту проблему , я recommed просто правопреемником 0к lastIndexчлену RegExpобъекта. У этого есть лучшая производительность, чем у уничтожения и воссоздания.
  2. Будьте осторожны при использовании inключевого слова для обходаArray объекта, поскольку это может привести к неожиданным результатам с некоторыми библиотеками. Иногда вам следует проверить что-нибудь вроде isNaN(i), или, если вы знаете, что в нем нет дыр, используйте классический цикл for.

Код может быть:

var rx = /(cat|dog)/gi;
w = ["I have a cat and a dog too.", "There once was a dog and a cat.", "I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat.","I have a cat and a dog too.", "There once was a dog and a cat."];

for (var i in w)
 if(!isNaN(i))        // Optional, check it is an element if Array could have some odd members.
  {
   var m = null;
   m = rx.exec(w[i]); // Run
   rx.lastIndex = 0;  // Reset
   if(m)
    {
     document.writeln("<pre>" + i + "\nINPUT: " + w[i] + "\nMATCHES: " + m.slice(1) + "</pre>");
    } else {
     document.writeln("<pre>" + i + "\n'" + w[i] + "' FAILED.</pre>");
    }
  }
ESL
источник
1
Это должен быть правильный ответ. Установка rx.lastIndex = 0намного лучше, чем воссоздание объекта RegEx внутри цикла.
Минору
4

У меня была аналогичная проблема с использованием только / g, и предлагаемое здесь решение не сработало для меня в FireFox 3.6.8. Мой сценарий работает с

var myRegex = new RegExp("my string", "g");

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

Дон
источник