javascript - De ce un RegExp cu steagul global oferă rezultate greșite?
Care este problema cu această expresie regulată când folosesc flag-ul global și flag-ul insensibil la majuscule? Interogarea este o intrare generată de utilizator. Rezultatul ar trebui să fie [true, true].
var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));
result.push(re.test('Foo Bar'));
// result will be [true, false]
var reg = /^a$/g;
for(i = 0; i++ < 10;)
console.log(reg.test("a"));Răspuns #1
Un obiect RegExp cu flag-ul g păstrează evidența lastIndex unde a avut loc o potrivire, așa că la potrivirile ulterioare va începe de la ultimul index folosit, în loc de 0. Aruncă o privire:
var query = 'Foo B';
var re = new RegExp(query, 'gi');
console.log(re.lastIndex);
console.log(re.test('Foo Bar'));
console.log(re.lastIndex);
console.log(re.test('Foo Bar'));
console.log(re.lastIndex);Dacă nu vrei să resetezi manual lastIndex la 0 după fiecare test, pur și simplu elimină flagul g.
Iată algoritmul pe care specificațiile îl impun (secțiunea 15.10.6.2):
RegExp.prototype.exec(string)
Efectuează o potrivire a expresiei regulate a șirului împotriva expresiei regulate și returnează un obiect Array care conține rezultatele potrivirii sau null dacă șirul nu s-a potrivit. Șirul ToString(string) este căutat pentru o apariție a modelului expresiei regulate după cum urmează:
- Fie R acest obiect RexExp.
- Fie S valoarea lui ToString(string).
- Fie length lungimea lui S.
- Fie lastIndex valoarea proprietății lastIndex din R.
- Fie i valoarea lui ToInteger(lastIndex).
- Dacă proprietatea globală este falsă, să fie i=0.
- Dacă i < 0 sau i > lungime atunci setează proprietatea lastIndex a R la 0 și returnează null.
- Apelează [[Match]], oferindu-i argumentele S și i. Dacă [[Match]] a returnat eșec, mergi la pasul 9; altfel, lasă ca r să fie rezultatul său State și mergi la pasul 10.
- Fie i=i+1.
- Mergeți la pasul 7.
- Fie e valoarea endIndex a lui r.
- Dacă proprietatea globală este adevărată, setează proprietatea lastIndex a lui R la e.
- Fie n lungimea matricei de capturi a lui r. (Aceasta este aceeași valoare ca NCapturingParens din 15.10.2.1.)
- Returnează un nou tablou cu următoarele proprietăți:
- Proprietatea index este setată la poziția subșirului găsit în cadrul șirului complet S.
- Proprietatea de intrare este setată la S.
- Proprietatea length este setată la n + 1.
- Proprietatea 0 este setată la subșirul corespunzător (adică porțiunea din S între decalajul i inclusiv și decalajul e exclusiv).
- Pentru fiecare număr întreg i astfel încât i > 0 și i ≤ n, setează proprietatea numită ToString(i) la al i-lea element din tabloul de capturi al lui r.
Răspunsul #2
Folosești un singur obiect RegExp și îl execuți de mai multe ori. La fiecare execuție succesivă continuă de la ultimul indice de potrivire.
Trebuie să „resetați” expresia regulată pentru a începe de la început înainte de fiecare execuție:
result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
// result is now [true, true]
Spunând asta, poate fi mai ușor de citit să creezi un nou obiect RegExp de fiecare dată (suprasarcina este minimă deoarece RegExp este oricum memorat în cache):
result.push((/Foo B/gi).test(stringA));
result.push((/Foo B/gi).test(stringB));
Răspunsul #3
RegExp.prototype.test actualizează proprietatea lastIndex a expresiilor regulate astfel încât fiecare test să înceapă de unde s-a oprit ultimul. Aș sugera utilizarea String.prototype.match deoarece nu actualizează proprietatea lastIndex:
!!'Foo Bar'.match(re); // -> true
!!'Foo Bar'.match(re); // -> true
Notă: !! îl convertește într-un boolean și apoi inversează booleanul astfel încât să reflecte rezultatul.
Alternativ, ai putea pur și simplu să resetezi proprietatea lastIndex:
result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
Răspunsul #4
Îndepărtarea flag-ului global g va rezolva problema ta.
var re = new RegExp(query, 'gi');
Ar trebui să fie
var re = new RegExp(query, 'i');
