javascript - Mengapa RegExp dengan flag global menghasilkan hasil yang salah?
Apa masalah dengan ekspresi reguler ini ketika saya menggunakan flag global dan flag tidak sensitif huruf? Query adalah input yang dihasilkan pengguna. Hasilnya seharusnya [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"));Jawaban #1
Sebuah objek RegExp dengan flag g melacak lastIndex di mana kecocokan terjadi, jadi pada kecocokan berikutnya ia akan mulai dari indeks terakhir yang digunakan, bukan dari 0. Lihatlah:
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);Jika Anda tidak ingin mengatur ulang lastIndex ke 0 secara manual setelah setiap pengujian, cukup hapus flag g.
Berikut adalah algoritma yang ditentukan oleh spesifikasi (bagian 15.10.6.2):
RegExp.prototype.exec(string)
Melakukan pencocokan ekspresi reguler dari string terhadap ekspresi reguler dan mengembalikan objek Array yang berisi hasil pencocokan, atau null jika string tidak cocok. String ToString(string) dicari untuk kemunculan pola ekspresi reguler sebagai berikut:
- Biarkan R menjadi objek RexExp ini.
- Misalkan S adalah nilai dari ToString(string).
- Biarkan length menjadi panjang dari S.
- Biarkan lastIndex menjadi nilai dari properti lastIndex pada R.
- Biarkan i menjadi nilai dari ToInteger(lastIndex).
- Jika properti global bernilai salah, biarkan i=0.
- Jika i < 0 atau i > panjang maka atur properti lastIndex dari R menjadi 0 dan kembalikan null.
- Panggil [[Match]], dengan memberikan argumen S dan i. Jika [[Match]] mengembalikan kegagalan, lanjutkan ke langkah 9; jika tidak, biarkan r menjadi hasil State-nya dan lanjutkan ke langkah 10.
- Biarkan i=i+1.
- Lanjut ke langkah 7.
- Biarkan e menjadi nilai endIndex dari r.
- Jika properti global bernilai true, atur properti lastIndex dari R ke e.
- Biarkan n menjadi panjang dari array tangkapan r. (Ini adalah nilai yang sama dengan NCapturingParens pada 15.10.2.1.)
- Kembalikan array baru dengan properti berikut:
- Properti indeks diatur ke posisi substring yang cocok dalam string lengkap S.
- Properti input diatur ke S.
- Properti panjang diatur ke n + 1.
- Properti 0 diatur ke substring yang cocok (yaitu bagian dari S antara offset i inklusif dan offset e eksklusif).
- Untuk setiap bilangan bulat i sehingga i > 0 dan i ≤ n, tetapkan properti yang bernama ToString(i) ke elemen ke-i dari array tangkapan r.
Jawaban #2
Anda menggunakan satu objek RegExp dan mengeksekusinya beberapa kali. Pada setiap eksekusi berikutnya, itu terus dari indeks kecocokan terakhir.
Anda perlu "mengatur ulang" regex untuk memulai dari awal sebelum setiap eksekusi:
result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
// result is now [true, true]
Dengan demikian, mungkin akan lebih mudah dibaca jika membuat objek RegExp baru setiap kali (beban tambahan minimal karena RegExp tetap disimpan dalam cache):
result.push((/Foo B/gi).test(stringA));
result.push((/Foo B/gi).test(stringB));
Jawaban #3
RegExp.prototype.test memperbarui properti lastIndex dari ekspresi reguler sehingga setiap pengujian akan dimulai dari posisi terakhir berhenti. Saya menyarankan menggunakan String.prototype.match karena metode ini tidak memperbarui properti lastIndex:
!!'Foo Bar'.match(re); // -> true
!!'Foo Bar'.match(re); // -> true
Catatan: !! mengubahnya menjadi boolean dan kemudian membalikkan boolean tersebut sehingga mencerminkan hasilnya.
Sebagai alternatif, Anda bisa saja mereset properti lastIndex:
result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
Jawaban #4
Menghapus flag global g akan memperbaiki masalah Anda.
var re = new RegExp(query, 'gi');
Seharusnya
var re = new RegExp(query, 'i');
