Závěrem tohoto blogového příspěvku je, že Internet Explorer nesprávně zvyšuje hodnotu 09
regulárního objektu. vlastnost po úspěšné shodě s nulovou délkou. Nicméně pro každého, kdo si není jistý, o čem mluvím, nebo se zajímá o to, jak problém obejít, popíšu problém s příklady opakování každé shody v řetězci pomocí 11 metoda. Právě tam jsem se s chybou setkal nejčastěji a myslím, že to pomůže vysvětlit, proč problém existuje.
Za prvé, pokud ještě nevíte, jak používat 28
iterovat přes řetězec, přicházíte o některé velmi výkonné funkce. Zde je základní konstrukce:
var regex = /.../g, subject = "test", match = regex.exec(subject); while (match != null) { // matched text: match[0] // match start: match.index // match end: regex.lastIndex // capturing group n: match[n] ... match = regex.exec(subject); }
Když 38
metoda je volána pro regex, který používá 45
(globální) modifikátor, vyhledává od bodu v řetězci předmětu určeného v regulárním výrazu 53
vlastnost (která je zpočátku nula, takže hledá od začátku řetězce). Pokud 68
metoda najde shodu, aktualizuje 72
regulárního výrazu vlastnost na znakový index na konci shody a vrátí pole obsahující odpovídající text a všechny zachycené podvýrazy. Pokud od bodu v řetězci, kde hledání začalo, žádná shoda, 85
je resetováno na nulu a 97
je vráceno.
Výše uvedený kód můžete zpřesnit přesunutím 104
volání metody do 117
stav smyčky, například takto:
var regex = /.../g, subject = "test", match; while (match = regex.exec(subject)) { ... }
Tato čistší verze funguje v podstatě stejně jako předchozí. Jakmile 124
nemůže najít žádné další shody, a proto vrátí 135
, smyčka končí. U obou verzí tohoto kódu však existuje několik problémů s různými prohlížeči. Jedním z nich je, že pokud regulární výraz obsahuje zachycující skupiny, které se neúčastní shody, některé hodnoty ve vráceném poli mohou být buď 140
nebo prázdný řetězec. O tomto problému jsem již dříve podrobně diskutoval v příspěvku o tom, co jsem nazval nezúčastněné zachytávající skupiny.
Další problém (téma tohoto post) nastane, když váš regulární výraz odpovídá prázdnému řetězci. Existuje mnoho důvodů, proč byste to mohli regulárnímu výrazu povolit, ale pokud vás žádný nenapadá, zvažte případy, kdy přijímáte regulární výrazy z vnějšího zdroje. Zde je jednoduchý příklad takového regulárního výrazu:
var regex = /^/gm, subject = "A\nB\nC", match, endPositions = []; while (match = regex.exec(subject)) { endPositions.push(regex.lastIndex); }
Můžete očekávat 158
pole, které má být nastaveno na 167
, protože to jsou pozice znaků na začátku řetězce a hned za každým znakem nového řádku. Díky 176
modifikátor, to jsou pozice, kde se regulární výraz bude shodovat; a protože regulární výraz odpovídá prázdným řetězcům, 180
by měl být stejný jako 199
. Internet Explorer (testováno s v5.5–7) však nastavuje 203
na 218
. Ostatní prohlížeče přejdou do nekonečné smyčky, dokud nezkratujete kód.
Tak co se tu děje? Pamatujte si to pokaždé, když 222
běží, pokusí se najít shodu v řetězci předmětu počínaje pozicí určenou 234
vlastnost regulárního výrazu. Protože náš regulární výraz odpovídá řetězci nulové délky, 243
zůstává přesně tam, kde jsme hledání začali. Při každém průchodu smyčkou se tedy náš regulární výraz bude shodovat na stejné pozici – na začátku řetězce. Internet Explorer se snaží být nápomocný a této situaci se vyhnout automatickým zvýšením 255
když se shoduje řetězec nulové délky. To se může zdát jako dobrý nápad (ve skutečnosti jsem viděl lidi neústupně argumentovat, že jde o chybu, kterou Firefox nedělá stejně), ale znamená to, že v Internet Exploreru je 266
nelze spoléhat na přesné určení konečné pozice zápasu.
Tuto situaci můžeme napravit v různých prohlížečích pomocí následujícího kódu:
var regex = /^/gm, subject = "A\nB\nC", match, endPositions = []; while (match = regex.exec(subject)) { var zeroLengthMatch = !match[0].length; // Fix IE's incorrect lastIndex if (zeroLengthMatch && regex.lastIndex > match.index) regex.lastIndex--; endPositions.push(regex.lastIndex); // Avoid an infinite loop with zero-length matches if (zeroLengthMatch) regex.lastIndex++; }
Příklad výše uvedeného kódu můžete vidět v metodě rozdělení mezi prohlížeči, kterou jsem před časem zveřejnil. Mějte na paměti, že zde není potřeba žádný další kód, pokud váš regulární výraz nemůže odpovídat prázdnému řetězci.
Dalším způsobem, jak tento problém vyřešit, je použít 279
iterovat přes předmětový řetězec. 283
metoda se po shodách s nulovou délkou automaticky posune vpřed, čímž se tomuto problému zcela vyhne. Bohužel ve třech největších prohlížečích (IE, Firefox, Safari) 293
nezdá se, že by se zabýval 305
vlastnost kromě resetování na nulu. Opera to udělá správně (podle mého čtení specifikace) a aktualizuje 317
při cestě. Vzhledem k současné situaci se nemůžete na 326
spolehnout ve vašem kódu při iteraci přes řetězec pomocí 331
, ale přesto můžete snadno odvodit hodnotu pro konec každého zápasu. Zde je příklad:
var regex = /^/gm, subject = "A\nB\nC", endPositions = []; subject.replace(regex, function (match) { // Not using a named argument for the index since capturing // groups can change its position in the list of arguments var index = arguments[arguments.length - 2], lastIndex = index + match.length; endPositions.push(lastIndex); });
To je možná méně přehledné než dříve (protože ve skutečnosti nic nenahrazujeme), ale tady to máte… dva způsoby, jak obejít málo známý problém, který by jinak mohl způsobit záludné, latentní chyby ve vašem kódu.