อะไรคือความแตกต่างระหว่างฟังก์ชัน exec () ของ RegExp และฟังก์ชัน Match () ของ String?


122

ถ้าฉันเรียกใช้สิ่งนี้:

/([^\/]+)+/g.exec('/a/b/c/d');

ฉันได้รับสิ่งนี้:

["a", "a"]

แต่ถ้าฉันเรียกใช้สิ่งนี้:

'/a/b/c/d'.match(/([^\/]+)+/g);

จากนั้นฉันจะได้ผลลัพธ์ที่คาดหวังจากสิ่งนี้:

["a", "b", "c", "d"]

อะไรคือความแตกต่าง?


4
คุณวนซ้ำexecเพื่อรับตัวเลือกย่อยทั้งหมด
zzzzBov

2
โปรดทราบว่าสิ่งที่สอง+ไม่จำเป็นเนื่องจากmatchจะส่งคืนนิพจน์ย่อยทั้งหมดแล้ว .execส่งคืนเพียงครั้งเดียวในแต่ละครั้งดังนั้นจึงไม่จำเป็นต้องใช้+เช่นกัน
pimvdb

3
ยิ่งไปกว่านั้นตัวระบุจำนวนที่ซ้อนกันเช่นบวกทั้งสองควรใช้อย่างระมัดระวังอย่างยิ่งเพราะจะนำไปสู่การย้อนรอยที่หายนะได้ง่าย
Marius Schulz

1
@MariusSchulz ขอบคุณสำหรับลิงค์ นั่นทำให้ฉันได้เรียนรู้เกี่ยวกับตัวระบุปริมาณและการจัดกลุ่มอะตอม สิ่งที่ดีมากที่จะเข้าใจ
Justin Warkentin

คำตอบ:


118

execด้วยนิพจน์ทั่วไปส่วนกลางมีไว้เพื่อใช้ในการวนซ้ำเนื่องจากจะยังคงดึงนิพจน์ย่อยที่ตรงกันทั้งหมด ดังนั้น:

var re = /[^\/]+/g;
var match;

while (match = re.exec('/a/b/c/d')) {
    // match is now the next match, in array form.
}

// No more matches.

String.match ทำสิ่งนี้ให้คุณและทิ้งกลุ่มที่ถูกจับ


39
ฉันมีบางอย่างที่จะเพิ่มในคำตอบนี้ไม่ควรวางนิพจน์ทั่วไปภายในเงื่อนไข while เช่นนี้มิwhile(match = /[^\/]+/g.exec('/a/b/c/d')ฉะนั้นจะสร้างลูปที่ไม่มีที่สิ้นสุด! ตามที่ระบุไว้ชัดเจนใน MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
yeyo

7
@yeyo: โดยเฉพาะอย่างยิ่งมันต้องเป็นวัตถุนิพจน์ทั่วไปเดียวกัน ตัวอักษรไม่สามารถบรรลุสิ่งนั้นได้
Ry-

@ Ry- ฉันคิดว่าควรสังเกตพฤติกรรมนี้ใน ES5 ก่อน ES5 new RegExp("pattern")และ/pattern/หมายถึงสิ่งที่แตกต่างกัน
Robert

75

รูปเดียวดีกว่านะรู้ยัง ...

re_once = /([a-z])([A-Z])/
re_glob = /([a-z])([A-Z])/g

st = "aAbBcC"

console.log("match once="+ st.match(re_once)+ "  match glob="+ st.match(re_glob))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))

ดูความแตกต่าง?

หมายเหตุ:หากต้องการเน้นให้สังเกตว่ากลุ่มที่จับได้ (เช่น a, A) จะถูกส่งคืนหลังจากรูปแบบที่ตรงกัน (เช่น aA) ไม่ใช่แค่รูปแบบที่ตรงกันเท่านั้น


28

/regex/.exec()ส่งคืนเฉพาะการจับคู่แรกที่พบในขณะที่"string".match()ส่งคืนค่าทั้งหมดหากคุณใช้gแฟล็กใน regex

ดูที่นี่: exec , match .


23

หาก regex ของคุณเป็นแบบโกลบอลและคุณกำลังจับคุณต้องใช้ exec การจับคู่จะไม่ส่งคืนการจับทั้งหมดของคุณ

การจับคู่ใช้งานได้ดีเมื่อจับคู่ (ไม่จับคู่) คุณเรียกใช้ครั้งเดียวและจะให้อาร์เรย์ของการแข่งขันทั้งหมด (แม้ว่า regex จะไม่ใช่ global การจับคู่จะแสดงการจับคู่ตามด้วยการจับ)

Exec คือสิ่งที่คุณใช้เมื่อคุณจับภาพและทุกครั้งที่ดำเนินการมันจะให้การจับคู่ตามด้วยการจับภาพ (การจับคู่จะทำงานในลักษณะของการจับคู่แบบเต็มตามด้วยการจับคู่เฉพาะเมื่อนิพจน์ทั่วไปไม่ใช่ส่วนกลาง)

การใช้งานอื่นกับ Exec คือการได้รับดัชนีหรือตำแหน่งของการแข่งขัน เมื่อคุณมีตัวแปรสำหรับ regex คุณสามารถใช้. LastIndex และรับตำแหน่งของการจับคู่ ออบเจ็กต์ regex มี. LastIndex และอ็อบเจ็กต์ regex คือสิ่งที่คุณทำ. exec การจับคู่จุดเสร็จสิ้นบนสตริงและคุณจะไม่สามารถทำ regex object dot lastIndex ได้

สตริงมีฟังก์ชันการจับคู่ซึ่งส่งผ่านนิพจน์ทั่วไป และ regex มีฟังก์ชัน exec และส่งผ่านสตริง

คุณเรียกใช้หลายครั้ง จับคู่คุณวิ่งครั้งเดียว

เป็นการดีที่จะใช้การจับคู่เมื่อไม่จับภาพและเมื่อจับภาพคุณสามารถใช้ exec ซึ่งมีประสิทธิภาพมากกว่าเนื่องจากดีในการจับภาพ แต่ถ้าคุณใช้การจับคู่เมื่อจับภาพให้ดูว่าจะแสดงการจับภาพเมื่อ regex ไม่ใช่ global แต่ไม่ ไม่แสดงการจับภาพเมื่อ regex เป็นแบบโกลบอล

> "azb".match(/a(z)b/);
[ "azb", "z" ]

> "azb".match(/a(z)b/g);
[ "azb" ]
>

อีกประการหนึ่งคือถ้าคุณใช้ exec ให้สังเกตว่าถูกเรียกใช้ใน regex แล้วถ้าคุณใช้ตัวแปรสำหรับ regex คุณจะมีอำนาจมากขึ้น

คุณไม่ได้รับการจับคู่เมื่อคุณไม่ใช้ตัวแปรสำหรับ regex ดังนั้นให้ใช้ตัวแปรสำหรับ regex เมื่อใช้ exec

> /./g.exec("abc")
[ "a" ]
> /./g.exec("abc")
[ "a" ]
> /./g.exec("abc")
[ "a" ]
>
> /[a-c]/g.exec("abc")
[ "a" ]
> /[a-c]/g.exec("abc")
[ "a" ]
>

> var r=/[a-c]/g
> r.exec("abc")
[ "a" ]
> r.exec("abc")
[ "b" ]
> r.exec("abc")
[ "c" ]
> r.exec("abc")
null
>

และด้วยผู้บริหารคุณจะได้รับ "ดัชนี" ของการแข่งขัน

> var r=/T/g
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
2
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
6
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
9
> r.exec("qTqqqTqqTq");
null
> r.lastIndex
0
>

ดังนั้นหากคุณต้องการดัชนีหรือการจับภาพให้ใช้ exec (โปรดจำไว้ว่าอย่างที่คุณเห็นเมื่อใช้ "ดัชนี" "ดัชนี" ที่ให้นั้นเป็นเหตุการณ์ที่ n นับจาก 1 ดังนั้นคุณจะได้ค่าที่เหมาะสม ดัชนีโดยการลบ 1 และอย่างที่คุณเห็นมันให้ 0 - lastIndex เป็น 0 - สำหรับไม่พบ)

และถ้าคุณต้องการยืดการจับคู่คุณสามารถใช้มันได้เมื่อคุณจับภาพ แต่ไม่ใช่เมื่อ regex เป็นโกลบอลและเมื่อคุณทำเช่นนั้นเนื้อหาของอาร์เรย์จะไม่ตรงทั้งหมด แต่เป็นแบบเต็ม จับคู่ตามด้วยการจับภาพ


ใช่การทำความเข้าใจการทำงานของr.lastIndexเป็นปัจจัยสำคัญที่จะเข้าใจความแตกต่างระหว่างและexec match
ดำเนินการใน

@barlop "การจับคู่จะไม่ตรงกับการจับทั้งหมด" อย่างจริงจัง? "a, b, c, aa, bb, cc" .match (/ (\ w +) / g) => ["a", "b", "c", "aa", "bb", "cc" ] จะอธิบายได้อย่างไรว่ามันแคชไว้ทั้งหมด?
MrHIDEn

@barlop If your regex is global, and you are capturing, then you must use exec. Match won't return all your captures.ฉันได้รับมันบนคอนโซล เพียงคัดลอก / วาง"a,b,c,aa,bb,cc".match(/(\w+)/g);Opera, Firefox
MrHIDEn

@MrHIDEn ฉันจะไม่ใช้ภาษาที่คุณพูดผิด และสิ่งที่สำคัญคือสิ่งที่แสดงและสิ่งที่เราสามารถดู .. ว่ามีการแคชเบื้องหลังหรือไม่ก็ไม่เกี่ยวข้อง และเป็นเวลานานแล้วที่ฉันได้ตรวจสอบสิ่งนี้ แต่ก็ไม่ได้แสดงการจับภาพทั้งหมด .. แม้ว่าคุณจะทำตามตัวอย่าง"a,b,c,aa,bb,cc".match(/(\w+)/g) ก็ตามสิ่งที่เกิดขึ้นมันจะแสดงการแข่งขันทั้งหมดและมันก็เกิดขึ้นที่คุณจับได้ทุกนัดดังนั้น ถ้าจะแสดงการจับภาพทั้งหมดมันจะเหมือนกันทุกประการ (cntd)
barlop

(cntd) บางทีคุณอาจคิดว่ามันแสดงการจับภาพ แต่มันไม่ใช่มันแสดงการแข่งขัน
barlop

6

.match ()ฟังก์ชั่นstr.match(regexp)จะทำต่อไปนี้:

  • ถ้ามีก็คือการแข่งขันก็จะกลับ:
    • ถ้าgธงถูกนำมาใช้ใน regexp: มันจะกลับสตริงทั้งหมด(ไม่สนใจกลุ่มจับภาพ)
    • หากไม่ได้ใช้gแฟล็กใน regexp: มันจะส่งคืนเหมือนเดิมregexp.exec(str)
  • หากไม่มีการแข่งขันจะส่งคืน:
    • null

ตัวอย่างของ. match ()โดยใช้gแฟล็ก :

var str = "qqqABApppabacccaba";
var e1, e2, e3, e4, e5;
e1 = str.match(/nop/g); //null
e2 = str.match(/no(p)/g); //null
e3 = str.match(/aba/g); //["aba", "aba"]
e4 = str.match(/aba/gi); //["ABA", "aba", "aba"]
e5 = str.match(/(ab)a/g); //["aba", "aba"] ignoring capture groups as it is using the g flag

และ. match () ที่ไม่มีgแฟล็กจะเทียบเท่ากับ. exec () :

e1=JSON.stringify(str.match(/nop/))===JSON.stringify(/nop/.exec(str)); //true
//e2 ... e4 //true
e5=JSON.stringify(str.match(/(ab)a/))===JSON.stringify(/(ab)a/.exec(str)); //true

.exec ()ฟังก์ชั่นregexp.exec(str)จะทำต่อไปนี้:

  • ถ้ามีก็คือการแข่งขันก็จะกลับ:
    • ถ้าgธงถูกนำมาใช้ใน regexp: มันจะกลับมา(แต่ละครั้งจะเรียกว่า) : [N_MatchedStr, N_Captured1, N_Captured2, ...]การต่อไปNการแข่งขัน สำคัญ:มันจะไม่เข้าสู่การจับคู่ครั้งต่อไปหากวัตถุ regexp ไม่ได้ถูกเก็บไว้ในตัวแปร (จำเป็นต้องเป็นวัตถุเดียวกัน)
    • หากไม่ได้ใช้gแฟล็กใน regexp จะส่งคืนเหมือนเดิมราวกับว่ามีแฟล็กและถูกเรียกเป็นครั้งแรกและครั้งเดียวg
  • หากไม่มีการแข่งขันจะส่งคืน:
    • null

ตัวอย่างของ. exec () (จัดเก็บ regexp + โดยใช้gแฟล็ก = มันเปลี่ยนไปตามการเรียกแต่ละครั้ง):

var str = "qqqABApppabacccaba";
var myexec, rgxp = /(ab)a/gi;

myexec = rgxp.exec(str);
console.log(myexec); //["ABA", "AB"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "ab"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "ab"]
myexec = rgxp.exec(str);
console.log(myexec); //null

//But in this case you should use a loop:
var mtch, myRe = /(ab)a/gi;
while(mtch = myRe.exec(str)){ //infinite looping with direct regexps: /(ab)a/gi.exec()
    console.log("elm: "+mtch[0]+" all: "+mtch+" indx: "+myRe.lastIndex);
    //1st iteration = elm: "ABA" all: ["ABA", "AB"] indx: 6
    //2nd iteration = elm: "aba" all: ["aba", "ab"] indx: 12
    //3rd iteration = elm: "aba" all: ["aba", "ab"] indx: 18
}

ตัวอย่างของ. exec ()เมื่อไม่มีการเปลี่ยนแปลงในการเรียกแต่ละครั้ง:

var str = "qqqABApppabacccaba", myexec, myexec2;

//doesn't go into the next one because no g flag
var rgxp = /(a)(ba)/;
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "a", "ba"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "a", "ba"]
//... ["aba", "a", "ba"]

//doesn't go into the next one because direct regexp
myexec2 = /(ab)a/gi.exec(str);
console.log(myexec2); //["ABA", "AB"]
myexec2 = /(ab)a/gi.exec(str);
console.log(myexec2); //["ABA", "AB"]
//... ["ABA", "AB"]

1

บางครั้ง regex.exec () จะใช้เวลามากเวลามากขึ้นแล้ว string.match ()

ควรกล่าวถึงว่าหากผลลัพธ์ของ string.match () และ regex.exec () เหมือนกัน (เช่น: เมื่อไม่ใช้แฟล็ก \ g) regex.exec () จะอยู่ระหว่าง x2 ถึง x30 จากนั้นสตริง การจับคู่():

ดังนั้นในกรณีเช่นนี้การใช้แนวทางของ "RegExp ใหม่ (). exec ()" ควรใช้เมื่อคุณต้องการ regex ส่วนกลางเท่านั้น (กล่าวคือดำเนินการมากกว่าหนึ่งครั้ง)


1
คุณมีเกณฑ์มาตรฐานหรือไม่?
SơnTrần-Nguyễn
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.