RegExp.exec () sporadycznie zwraca NULL


87

Naprawdę szaleję z tego powodu i już spędziłem nieproporcjonalną ilość czasu, próbując dowiedzieć się, co się tutaj dzieje. Więc proszę, pomóż mi =)

Muszę zrobić dopasowanie RegExp ciągów w JavaScript. Niestety zachowuje się bardzo dziwnie. Ten kod:

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>");
    }
}

Zwraca „kot” i „pies” dla pierwszych dwóch elementów, tak jak powinno, ale potem exec()zaczynają wracać niektóre wywołania null. Nie rozumiem dlaczego.

Umieściłem tutaj Fiddle , gdzie możesz uruchomić i edytować kod.

Do tej pory próbowałem tego w Chrome i Firefox.

Twoje zdrowie!

/ Christofer


to nie tylko w "I have a cat and a dog too.", jak się wydaje
SilentGhost

exec zwraca wartość null, jeśli dopasowanie nie powiedzie się zgodnie z projektem, więc z jakiegoś powodu nie pasuje.
Martin Jespersen

Odpowiedzi:


84

Och, oto jest. Ponieważ definiujesz swoje wyrażenie regularne globalne, dopasowuje się ono najpierw cati przy drugim przebiegu pętli dog. Więc w zasadzie wystarczy zresetować swoje wyrażenie regularne (jest to wewnętrzny wskaźnik). Por. to:

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);
}

mamy to, byłem za wolny :)
Martin Jespersen

ach słodko! zajęłoby mi trochę czasu, zanim to rozgryzłem. dzięki!
cpak

To zaoszczędziło mi dużo czasu. Dzięki wielkie!
Thomas Johansen,

Ten problem sprawia, że ​​wątpię w życie.
GZ Xue

Czuję, że powinienem po prostu zwrócić swoją wypłatę
cgatian

74

Obiekt regex ma właściwość, lastIndexktóra jest aktualizowana podczas uruchamiania exec. Więc kiedy wykonujesz wyrażenie regularne na np. „Mam kota i psa też.”, lastIndexJest ustawione na 12. Następnym razem, gdy uruchomisz execten sam obiekt wyrażenia regularnego, zacznie on szukać od indeksu 12. Więc musisz zresetować lastIndexwłaściwość między każdym biegiem.


Ta strona jest dla mnie za szybka. +1 dla SilentGhost :-)
Frode

10
Dziękuję za wyjaśnienie! Bardzo pomaga, ustawiając myRe.lastIndex = 0;do późniejszego użytku.
Antony,

1
Wow, bardzo dziękuję za podpowiedź z lastIndex, to naprawdę doprowadzało mnie do szału!
dave0688,

1
Myślę, że to powinna być poprawna odpowiedź, ponieważ jest to najlepsza praktyka ponownego użycia tego samego obiektu wyrażenia regularnego
smurtagh

Zgadzam się, że to powinna być prawidłowa odpowiedź. Ponownie wykorzystuje ten sam obiekt regex, a także wyjaśnia mechanikę wewnętrzną. OP powinien rozważyć zmianę.
Sean Coley

34

Dwie rzeczy:

  1. Wspomniana potrzeba resetowania przy użyciu gflagi (globalnej). Aby rozwiązać ten problem, polecam po prostu przypisać 0do lastIndexczłonka RegExpobiektu. Ma to lepszą wydajność niż niszcz i odtwarzaj.
  2. Zachowaj ostrożność podczas używania insłowa kluczowego do chodzenia poArray obiekcie, ponieważ może to prowadzić do nieoczekiwanych wyników w przypadku niektórych bibliotek. Czasami powinieneś sprawdzić coś takiego jak isNaN(i), lub jeśli wiesz, że nie ma dziur, użyj klasycznej pętli for.

Kod może być:

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>");
    }
  }

1
To powinna być prawidłowa odpowiedź. Ustawienie rx.lastIndex = 0jest znacznie lepsze niż ponowne tworzenie obiektu RegEx wewnątrz pętli.
Minoru

4

Miałem podobny problem używając tylko / g, a proponowane tutaj rozwiązanie nie działało u mnie w FireFox 3.6.8. Mam mój scenariusz

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

Dodam to na wypadek, gdyby ktoś inny miał ten sam problem, co w przypadku powyższego rozwiązania.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.