2013-08-27 16 views
15

Definiuję obiekt regex, a następnie dopasowuję go do pętli. Dopasowuje się tylko czasami, a dokładnie - co drugi raz. Dlatego stworzyłem najmniejszą próbną próbkę tego problemu.Dlaczego regeks Javascript jest dopasowywany co drugi raz?

Próbowałem tego kodu w Opera i Firefox. Zachowanie jest takie samo w obu przypadkach:

>>> 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 

Dlaczego tak się dzieje? Czy to zachowanie jest udokumentowane? Czy istnieje sposób na obejście, poza definiowaniem korpusu pętli wewnętrznej regex?

+3

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec# Finding_successive_matches – Passerby

+1

możliwy duplikat [Dlaczego RegExp z globalną flagą w JavaScript daje złe wyniki?] (Http://stackoverflow.com/questions/1520800/why-regexp-with-global-flag-in-javascript-give-wrong-results) – Bergi

+0

@GDR dzieje się tak z powodu [RegExp.lastIndex] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex?redirectlocale=en-US&redirectslug=JavaScript % 2FReference% 2FGlobal_Objects% 2FRegExp% 2FlastIndex) (_ przeczytaj "opis" section_). –

Odpowiedz

14

exec() działa w sposób opisany przez użytkownika; z teraźniejszością /g modyfikatora, zwróci mecz, począwszy od lastIndex przy każdym wywołaniu dopóki nie ma więcej meczów, w którym momencie powraca null a wartość lastIndex jest resetowany do 0.

Jednakże, ponieważ masz zakotwiczone wyrażenie używając $ nie będzie więcej niż jeden mecz, dzięki czemu można używać zamiast String.match() i stracić /g modyfikator:

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

każdorazowym uruchomieniu exec metodę swojej regex to dostaje następny mecz .

Po dotarciu do końca ciągu zwraca null, aby poinformować Cię, że masz wszystkie dopasowania. Następnym razem zaczyna się ponownie od początku.

Ponieważ masz tylko jeden mecz (który zwraca tablicę pełnego dopasowania i dopasowanie z nawiasów), Po raz pierwszy wyrażenie regularne rozpoczyna wyszukiwanie od początku. Znajduje dopasowanie i zwraca je. Następnym razem dochodzi do końca i zwraca zero. Więc jeśli miał to w pętli, można zrobić coś takiego pętli wszystkich meczów:

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

to następnym razem, to znowu zaczyna się od pozycji 0.

„Czy istnieje odwrotnie?"

Cóż, jeśli wiesz, że jest tylko jeden mecz lub chcesz tylko pierwszego dopasowania, możesz zapisać wynik do zmiennej. Nie musisz ponownie używać .exec. Jeśli interesują Cię wszystkie mecze, musisz jechać, dopóki nie uzyskasz null.

0

dlaczego nie można użyć prostej metody dopasowania do łańcucha jak

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

Ponieważ ma złą wydajność, aby utworzyć nową instancję obiektu Regex z każdą iteracją pętli. – GDR

+0

@GDR dzięki za wyjaśnienie. – dirtydexter

2

Po przeprowadzeniu wyszukiwania globalnego z RegExp, metoda exec zaczyna dopasowanie początek w lastIndex property. Właściwość lastIndex jest ustawiana dla każdej inwokacji exec, a jest ustawiana do pozycji następującej po znalezieniu ostatniego dopasowania. Jeśli mecz się nie powiedzie, lastIndex zostanie zresetowany do , co spowoduje, że exec ponownie będzie pasować od początku.

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"] 
0

Dodatkowe informacje na Ja͢cks odpowiedź:

Można również ustawić lastIndex

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

lub po prostu utworzyć nowy regex dla każdej realizacji, co znajdę jeszcze czystsze

new RegExp(myRgx).exec(someString); 
Powiązane problemy