Sự khác biệt giữa hàm thi hành của RegExp và hàm match () của String là gì?


122

Nếu tôi chạy cái này:

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

Tôi hiểu điều này:

["a", "a"]

Nhưng nếu tôi chạy cái này:

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

Sau đó, tôi nhận được kết quả mong đợi của điều này:

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

Có gì khác biệt?


4
bạn lặp lại với execđể nhận được tất cả các lựa chọn phụ.
zzzzBov

2
Lưu ý rằng thứ hai +là không cần thiết vì matchnó sẽ trả về tất cả các biểu thức con. .execchỉ trả về một lần mỗi lần, vì vậy nó cũng không cần điều đó +.
pimvdb

3
Trên hết, các bộ định lượng lồng nhau như hai điểm cộng này nên được sử dụng cực kỳ cẩn thận vì chúng dễ dẫn đến tình trạng bẻ khóa ngược thảm khốc .
Marius Schulz

1
@MariusSchulz Cảm ơn vì liên kết. Điều đó dẫn tôi đến việc tìm hiểu về định lượng sở hữu và phân nhóm nguyên tử. Những điều rất tốt đẹp để hiểu.
Justin Warkentin

Câu trả lời:


117

execvới một biểu thức chính quy toàn cục có nghĩa là được sử dụng trong một vòng lặp, vì nó vẫn sẽ truy xuất tất cả các biểu thức con phù hợp. Vì thế:

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 làm điều này cho bạn và hủy các nhóm đã bắt.


39
Tôi có một cái gì đó để thêm vào câu trả lời này, không nên đặt biểu thức chính quy theo nghĩa đen trong điều kiện while, như thế này while(match = /[^\/]+/g.exec('/a/b/c/d')hoặc nó sẽ tạo ra một vòng lặp vô hạn !. Như nó đã được nêu rõ ràng trong MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
yeyo

7
@yeyo: Cụ thể hơn, nó phải là cùng một đối tượng biểu thức chính quy. Một nghĩa đen không thực hiện được điều đó.
Ry-

@ Ry- Tôi nghĩ người ta nên lưu ý hành vi này đã được giới thiệu trong ES5. Trước ES5 new RegExp("pattern")/pattern/có nghĩa là những điều khác nhau.
Robert

75

Một bức ảnh đẹp hơn, bạn biết đấy ...

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))

Thấy sự khác biệt?

Lưu ý: Để làm nổi bật, hãy lưu ý rằng các nhóm đã bắt (ví dụ: a, A) được trả về sau mẫu phù hợp (ví dụ: aA), nó không chỉ là mẫu phù hợp.


28

/regex/.exec()chỉ trả về kết quả phù hợp đầu tiên được tìm thấy, trong khi "string".match()trả về tất cả chúng nếu bạn sử dụng gcờ trong regex.

Xem ở đây: exec , phù hợp .


23

Nếu regex của bạn là toàn cầu và bạn đang nắm bắt, thì bạn phải sử dụng tệp thi hành. Đối sánh sẽ không trả lại tất cả ảnh chụp của bạn.

Đối sánh hoạt động tốt khi chỉ khớp (không chụp). Bạn chạy nó một lần và nó cung cấp một loạt các kết quả phù hợp. (mặc dù nếu regex không phải là toàn cục, thì đối sánh sẽ hiển thị đối sánh sau đó là các lần chụp)

Exec là những gì bạn sử dụng khi chụp và mỗi lần thực thi nó sẽ cho kết quả khớp, sau đó là các lần chụp. (trận đấu sẽ hoạt động theo cách cung cấp kết quả khớp đầy đủ sau đó là các lần chụp, chỉ khi regex không phải là toàn cục).

Một cách sử dụng khác với Exec, là lấy chỉ số hoặc vị trí của một trận đấu. Khi bạn có một biến cho regex của mình, bạn có thể sử dụng .lastIndex và nhận vị trí của khớp. Đối tượng regex có .lastIndex và đối tượng regex là những gì bạn làm .exec trên. Đối sánh dấu chấm được thực hiện trên một chuỗi và bạn sẽ không thể thực hiện đối tượng regex chấm lastIndex

Một chuỗi, có hàm đối sánh, được chuyển qua một regex. Và một regex, có hàm thực thi và được truyền vào một chuỗi

thực thi bạn chạy nhiều lần. phù hợp với bạn chạy một lần

Thật tốt khi sử dụng khớp khi không chụp và khi chụp, bạn có thể sử dụng tệp thực thi mạnh hơn vì nó tốt cho việc chụp, nhưng nếu bạn đã sử dụng khớp khi chụp, hãy thấy rằng nó hiển thị ảnh chụp khi regex không toàn cục, nhưng không 'không hiển thị ảnh chụp khi regex là toàn cầu.

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

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

Một điều khác là nếu bạn sử dụng tệp thực thi, lưu ý rằng nó được gọi trên regex, sau đó nếu bạn sử dụng một biến cho regex, bạn có nhiều quyền lực hơn

Bạn không nhận được kết quả phù hợp khi bạn không sử dụng biến cho regex, vì vậy hãy sử dụng biến cho regex, khi sử dụng thi hành

> /./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
>

Và với hành động, bạn có thể nhận được "chỉ mục" của trận đấu

> 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
>

Vì vậy, nếu bạn muốn lập chỉ mục hoặc thu thập, thì hãy sử dụng tệp thực thi (lưu ý rằng như bạn có thể thấy, với "chỉ mục", "chỉ mục" mà nó đưa ra thực sự là lần xuất hiện thứ n, nó được tính từ 1. Vì vậy, bạn có thể tính chỉ mục bằng cách trừ đi 1. Và như bạn có thể thấy, nó cho 0 - lastIndex là 0 - không tìm thấy).

Và nếu bạn muốn kéo dài so khớp, bạn có thể sử dụng nó khi bạn đang chụp, nhưng không phải khi regex là toàn cục và khi bạn làm điều đó cho điều đó, thì nội dung của mảng không phải là tất cả các kết quả phù hợp, nhưng là toàn bộ trận đấu theo sau là chụp.


Vâng, hiểu được hoạt động của r.lastIndexlà yếu tố quan trọng để hiểu sự khác biệt giữa execmatch.
runun

@barlop "Trận đấu sẽ không khớp với tất cả các ảnh chụp", nghiêm túc đấy chứ? "a, b, c, aa, bb, cc" .match (/ (\ w +) / g) => ["a", "b", "c", "aa", "bb", "cc" ]. Làm thế nào để giải thích rằng nó đã lưu trữ tất cả chúng?
MrHIDEn

@barlop If your regex is global, and you are capturing, then you must use exec. Match won't return all your captures.Tôi đã tải nó trên bảng điều khiển. Chỉ cần sao chép / dán "a,b,c,aa,bb,cc".match(/(\w+)/g);Opera, Firefox.
MrHIDEn

@MrHIDEn Tôi sẽ không sử dụng ngôn ngữ bạn đã làm trong trích dẫn sai của bạn. Và điều quan trọng là những gì được hiển thị và những gì chúng ta có thể thấy .. liệu có bất kỳ bộ nhớ đệm nào đằng sau hậu trường hay không cũng không liên quan. Và đã được một thời gian kể từ khi tôi xem xét vấn đề này, nhưng Nó không hiển thị tất cả các lần chụp .. Ngay cả khi bạn làm ví dụ của mình "a,b,c,aa,bb,cc".match(/(\w+)/g) Điều gì đang xảy ra ở đó, nó hiển thị tất cả các trận đấu và nó chỉ xảy ra rằng bạn đã chụp mọi trận đấu, vì vậy nếu nó là hiển thị tất cả ảnh chụp, nó sẽ trông giống hệt nhau (cntd)
barlop

(cntd) Vì vậy, có lẽ bạn đang nghĩ đến nó là hiển thị các ảnh chụp, nhưng nó không phải là, nó cho thấy các trận đấu
barlop

6

Các .match () chức năng str.match(regexp)sẽ làm như sau:

  • nếu có một trận đấu nó sẽ trở lại:
    • nếu gcờ được sử dụng trong regexp: nó sẽ trả về tất cả các chuỗi con (bỏ qua các nhóm nắm bắt)
    • nếu gcờ không được sử dụng trong regexp: nó sẽ trả về giống nhưregexp.exec(str)
  • nếu không có kết quả phù hợp, nó sẽ trả về:
    • null

Ví dụ về .match () sử dụng gcờ:

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 () không có gcờ tương đương với .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

Các .exec () chức năng regexp.exec(str)sẽ làm như sau:

  • nếu có một trận đấu nó sẽ trở lại:
    • nếu gcờ được sử dụng trong regexp: nó sẽ trả về (cho mỗi lần nó được gọi) : [N_MatchedStr, N_Captured1, N_Captured2, ...]của Ntrận đấu tiếp theo . Quan trọng: nó sẽ không chuyển sang trận đấu tiếp theo nếu đối tượng regexp không được lưu trữ trong một biến (nó cần phải là cùng một đối tượng)
    • nếu gcờ không được sử dụng trong regexp: nó sẽ trả về giống như khi có gcờ và được gọi lần đầu tiên và chỉ một lần.
  • nếu không có kết quả phù hợp, nó sẽ trả về:
    • null

Ví dụ về .exec () (regexp được lưu trữ + sử dụng gcờ = nó thay đổi theo mỗi lần gọi):

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
}

Ví dụ về .exec () khi nó không thay đổi với mỗi lần gọi:

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

Đôi khi regex.exec () sẽ mất nhiều thời gian hơn string.match ().

Điều đáng nói là nếu kết quả của string.match () và regex.exec () giống nhau (ví dụ: khi không sử dụng \ g flag), thì regex.exec () sẽ nằm trong khoảng từ x2 đến x30 rồi đến chuỗi. trận đấu():

Do đó, trong những trường hợp như vậy, việc sử dụng phương pháp tiếp cận "new RegExp (). Execute ()" chỉ nên được sử dụng khi bạn cần một regex toàn cục (tức là thực thi nhiều lần).


1
Bạn có điểm chuẩn không?
Sơn Trần-Nguyễn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.