javascript - ทำไม RegExp ที่มีแฟลก global ถึงให้ผลลัพธ์ผิดพลาด?
ปัญหาคืออะไรกับนิพจน์ปกตินี้เมื่อฉันใช้ธง global และธงไม่แยกตัวพิมพ์ใหญ่เล็ก? Query เป็นข้อมูลที่ผู้ใช้สร้างขึ้น ผลลัพธ์ควรเป็น [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"));คำตอบ #1
วัตถุ RegExp ที่มีแฟล็ก g จะติดตาม lastIndex ที่เกิดการจับคู่ล่าสุด ดังนั้นในการจับคู่ครั้งถัดไปมันจะเริ่มจากดัชนีที่ใช้ล่าสุด แทนที่จะเริ่มจาก 0 มาดูกัน:
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);หากคุณไม่ต้องการรีเซ็ต lastIndex เป็น 0 ด้วยตนเองหลังจากการทดสอบแต่ละครั้ง ให้ลบธง g ออก
นี่คือตัวอัลกอริทึ่มที่สเปคกำหนดไว้ (มาตรา 15.10.6.2):
RegExp.prototype.exec(string)
ทำการจับคู่สตริงกับนิพจน์ปกติและคืนค่าอ็อบเจ็กต์ Array ที่มีผลลัพธ์ของการจับคู่ หรือคืนค่า null หากสตริงไม่ตรงกัน สตริง ToString(string) จะถูกค้นหาการปรากฏของรูปแบบนิพจน์ปกติตามลำดับดังนี้:
- ให้ R เป็นอ็อบเจ็กต์ RexExp นี้
- ให้ S เป็นค่าของ ToString(string)
- ให้ length เป็นความยาวของ S
- ให้ lastIndex เป็นค่าของคุณสมบัติ lastIndex บน R
- ให้ i เป็นค่าของ ToInteger(lastIndex)
- ถ้าคุณสมบัติทั่วโลกเป็นเท็จ ให้ i=0
- ถ้า i < 0 หรือ i > ความยาว ให้ตั้งค่าคุณสมบัติ lastIndex ของ R เป็น 0 และส่งคืนค่า null
- เรียก [[Match]] โดยให้มันมีอาร์กิวเมนต์ S และ i หาก [[Match]] คืนค่าการล้มเหลว ให้ไปที่ขั้นตอนที่ 9; มิฉะนั้นให้ r เป็นผลลัพธ์ State ของมันและไปที่ขั้นตอนที่ 10
- ให้ i=i+1.
- ไปที่ขั้นตอนที่ 7
- ให้ e เป็นค่าของ endIndex ของ r
- ถ้าคุณสมบัติ global เป็นจริง ให้ตั้งค่าคุณสมบัติ lastIndex ของ R เป็น e
- ให้ n เป็นความยาวของอาร์เรย์ captures ของ r (นี่คือค่าที่เหมือนกับ NCapturingParens ของ 15.10.2.1)
- ส่งคืนอาเรย์ใหม่พร้อมคุณสมบัติดังต่อไปนี้:
- คุณสมบัติ index ถูกตั้งค่าเป็นตำแหน่งของสตริงย่อยที่ตรงกันภายในสตริงเต็ม S
- คุณสมบัติการป้อนข้อมูลถูกตั้งค่าเป็น S.
- คุณสมบัติ length ถูกตั้งค่าเป็น n + 1
- สมบัติ 0 ถูกตั้งค่าเป็นสตริงย่อยที่ตรงกัน (คือ ส่วนของ S ระหว่างตำแหน่ง offset i รวมถึงและตำแหน่ง offset e ไม่รวม)
- สำหรับจำนวนเต็มแต่ละค่า i ที่ i > 0 และ i ≤ n ให้ตั้งสมบัติที่มีชื่อว่า ToString(i) ให้เท่ากับองค์ประกอบอันดับที่ i ของอาเรย์ captures ของ r
คำตอบ #2
คุณกำลังใช้วัตถุ RegExp เพียงตัวเดียวและเรียกใช้งานหลายครั้ง ในแต่ละครั้งที่เรียกใช้งานต่อเนื่อง มันจะเริ่มจากดัชนีการจับคู่ล่าสุด
คุณจำเป็นต้อง "รีเซ็ต" regex เพื่อเริ่มต้นจากจุดเริ่มต้นก่อนการประมวลผลแต่ละครั้ง:
result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
// result is now [true, true]
อย่างไรก็ตาม อาจอ่านง่ายกว่าถ้าสร้างวัตถุ RegExp ใหม่ทุกครั้ง (ค่าใช้จ่ายเพิ่มเติมน้อยมากเนื่องจาก RegExp ถูกเก็บในแคชอยู่แล้ว):
result.push((/Foo B/gi).test(stringA));
result.push((/Foo B/gi).test(stringB));
คำตอบ #3
RegExp.prototype.test จะอัปเดตสมบัติ lastIndex ของนิพจน์ปกติเพื่อให้แต่ละการทดสอบเริ่มที่ตำแหน่งที่การทดสอบครั้งล่าสุดหยุด ผมแนะนำให้ใช้ String.prototype.match เนื่องจากมันไม่อัปเดตสมบัติ lastIndex:
!!'Foo Bar'.match(re); // -> true
!!'Foo Bar'.match(re); // -> true
หมายเหตุ: !! แปลงมันเป็นค่าบูลีนแล้วจึงกลับค่าบูลีนเพื่อให้สะท้อนผลลัพธ์.
อีกทางหนึ่ง คุณสามารถรีเซ็ตคุณสมบัติ lastIndex ได้เลย:
result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
คำตอบ #4
การเอาแฟลก g แบบทั่วโลกออกจะช่วยแก้ปัญหาของคุณได้
var re = new RegExp(query, 'gi');
ควรจะ
var re = new RegExp(query, 'i');
