Varför returnerar inte Javascript's regex.exec() alltid samma värde?


I Chrome- eller Firebug-konsolen:

reg = /ab/g
str = "abc"
reg.exec(str)
   ==> ["ab"]
reg.exec(str)
   ==> null
reg.exec(str)
   ==> ["ab"]
reg.exec(str)
   ==> null

Är exec på något sätt tillståndsberoende och beror på vad det returnerade förra gången? Eller är det bara en bugg? Jag kan inte få det att hända hela tiden. Till exempel, om 'str' ovan var "abc abc" händer det inte.

Svar #1

Ett JavaScript RegExp-objekt är tillståndsberoende.

När regexen är global, om du anropar en metod på samma regex-objekt, kommer den att börja från indexet efter slutet av den senaste matchen.

När inga fler matchningar hittas återställs indexet automatiskt till 0.

För att återställa det manuellt, ställ in egenskapen lastIndex.

reg.lastIndex = 0;

Detta kan vara en mycket användbar funktion. Du kan börja utvärderingen vid vilken punkt som helst i strängen om du vill, eller om du är i en loop kan du stoppa den efter önskat antal träffar.

Här är en demonstration av ett typiskt tillvägagångssätt för att använda regex i en loop. Det utnyttjar det faktum att exec returnerar null när det inte finns fler matchningar genom att utföra tilldelningen som loopens villkor.

var re = /foo_(\d+)/g,
    str = "text foo_123 more text foo_456 foo_789 end text",
    match,
    results = [];

while (match = re.exec(str))
    results.push(+match[1]);

DEMO: http://jsfiddle.net/pPW8Y/

Om du inte gillar placeringen av uppgiften kan loopen omarbetas, som till exempel så här...

var re = /foo_(\d+)/g,
    str = "text foo_123 more text foo_456 foo_789 end text",
    match,
    results = [];

do {
    match = re.exec(str);
    if (match)
        results.push(+match[1]);
} while (match);

DEMO: http://jsfiddle.net/pPW8Y/1/

Svar #2

Från MDN-dokumentation:

Om ditt reguljära uttryck använder flaggan "g" kan du använda metoden exec flera gånger för att hitta efterföljande matchningar i samma sträng. När du gör det startar sökningen vid delsträngen av str som anges av reguljära uttryckets lastIndex-egenskap (test kommer också att förflytta lastIndex-egenskapen).

Eftersom du använder g-flaggan fortsätter exec från den sista matchade strängen tills den når slutet (returnerar null), och börjar sedan om.

Personligen föredrar jag att gå på motsatt sätt med str.match(reg)

Svar #3

Flera matchningar

Om din regex behöver g-flaggan (global match), måste du återställa indexet (positionen för det senaste matchningen) genom att använda lastIndex-egenskapen.

reg.lastIndex=0;

Detta beror på att exec() kommer att stanna vid varje förekomst så att du kan köra igen på den återstående delen. Detta beteende finns också med test()) :

Om ditt reguljära uttryck använder flaggan "g", kan du använda exec-metoden flera gånger för att hitta efterföljande matchningar i samma sträng. När du gör det, börjar sökningen vid delsträngen av str som anges av det reguljära uttryckets lastIndex-egenskap (test kommer också att flytta lastIndex-egenskapen).

Enskild match

När det bara finns en möjlig matchning kan du helt enkelt skriva om ditt regex genom att utelämna g-flaggan, eftersom indexet automatiskt kommer att återställas till 0.

Relaterade artiklar