2013-08-27 26 views
15

Tôi đang xác định đối tượng regex và sau đó đối sánh nó trong một vòng lặp. Nó chỉ phù hợp với đôi khi, để được chính xác - mỗi lần thứ hai. Vì vậy, tôi tạo ra một mẫu làm việc nhỏ nhất của vấn đề này.Tại sao Javascript Regex khớp với nhau mỗi lần thứ hai?

Tôi đã thử mã này trong Opera và Firefox. Hành vi này giống nhau ở cả hai:

>>> domainRegex = /(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/g; 
/(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/g 
>>> domainRegex.exec('mail-we0-f174.google.com'); 
Array [".google.com", "google.com"] 
>>> domainRegex.exec('mail-we0-f174.google.com'); 
null 
>>> domainRegex.exec('mail-we0-f174.google.com'); 
Array [".google.com", "google.com"] 
>>> domainRegex.exec('mail-we0-f174.google.com'); 
null 
>>> domainRegex.exec('mail-we0-f174.google.com'); 
Array [".google.com", "google.com"] 
>>> domainRegex.exec('mail-we0-f174.google.com'); 
null 

Tại sao điều này xảy ra? Hành vi này có được ghi lại không? Có cách nào xung quanh, khác hơn là xác định regex bên trong vòng lặp cơ thể?

+3

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec# Finding_successive_matches – Passerby

+1

có thể trùng lặp của [Tại sao RegExp với cờ toàn cầu trong Javascript cho kết quả sai?] (Http://stackoverflow.com/questions/1520800/why-regexp-with-global-flag-in-javascript-give-wrong-results) – Bergi

+0

@GDR điều này xảy ra vì [RegExp.lastIndex] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex?redirectlocale=en-US&redirectslug=JavaScript % 2FReference% 2FGlobal_Objects% 2FRegExp% 2FlastIndex) (_ đọc "mô tả" section_). –

Trả lời

14

exec() hoạt động theo cách bạn đã mô tả; với hiện tại /g sửa đổi, nó sẽ trở lại một trận đấu, bắt đầu từ lastIndex với mỗi gọi đến khi không có trận đấu nhiều hơn, lúc này nó sẽ trả về null và giá trị của lastIndex được reset về 0.

Tuy nhiên, bởi vì bạn có neo biểu thức sử dụng $ sẽ không có nhiều hơn một trận, vì vậy bạn có thể sử dụng thay vì String.match() và mất /g modifier:

var domainRegex = /(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/; 
'mail-we0-f174.google.com'.match(domainRegex); // [".google.com", "google.com"] 
2

Mỗi khi bạn chạy các phương pháp exec của regex của bạn nó được cho bạn những trận đấu tiếp theo .

Khi nó đến cuối chuỗi, nó trả về null để cho bạn biết bạn đã có tất cả các kết quả phù hợp. Lần sau, nó bắt đầu lại từ đầu.

Vì bạn chỉ có một kết quả phù hợp (trả về một mảng khớp đầy đủ và khớp từ dấu ngoặc), Lần đầu tiên, regex bắt đầu tìm kiếm từ đầu. Nó tìm thấy một trận đấu và trả về nó. Lần sau, nó được kết thúc và trả về null. Vì vậy, nếu bạn có điều này trong một vòng lặp, bạn có thể làm một cái gì đó như thế này để lặp qua tất cả các trận đấu:

while(regExpression.exec(string)){ 
    // do something 
} 

Sau đó, thời gian tới, nó bắt đầu lại từ vị trí 0.

"Có cách xung quanh? "

Vâng, nếu bạn biết chỉ có một kết quả phù hợp hoặc bạn chỉ muốn kết hợp đầu tiên, bạn có thể lưu kết quả te vào một biến. Không cần phải sử dụng lại .exec. Nếu bạn quan tâm đến tất cả các trận đấu, thì bạn cần tiếp tục cho đến khi bạn nhận được null.

0

tại sao bạn không sử dụng phương pháp phù hợp đơn giản cho chuỗi như

'mail-we0-f174.google.com'.match(/(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/) 
+1

Bởi vì nó có hiệu suất kém để tạo một thể hiện mới của đối tượng Regex với mỗi lần lặp của một vòng lặp. – GDR

+0

@GDR cảm ơn vì đã giải thích. – dirtydexter

2

Khi thực hiện tìm kiếm toàn cầu với một RegExp, phương pháp exec bắt đầu phù hợp với đầu tại các lastIndex property. Thuộc tính lastIndex được đặt ở mỗi yêu cầu exec và được đặt thành vị trí sau trận đấu cuối cùng được tìm thấy. Nếu khớp không thành công, lastIndex được đặt lại thành , làm cho exec khớp với từ đầu một lần nữa.

var a = 'asdfeeeasdfeedxasdf' 
undefined 
var p = /asdf/g 
p.lastIndex 
4 
p.exec(a) 
["asdf"] 
p.lastIndex 
11 
p.exec(a) 
["asdf"] 
p.lastIndex 
19 
p.exec(a) 
null //match failed 
p.lastIndex 
0 //lastIndex reset. next match will start at the beginning of the string a 

p.exec(a) 
["asdf"] 
0

Thông tin bổ sung để Ja͢cks phản ứng:

Bạn cũng có thể thiết lập lastIndex

var myRgx = /test/g; 
myRgx.exec(someString); 
myRgx.lastIndex = 0; 

hoặc chỉ cần tạo một regex mới cho mỗi thực hiện, mà tôi tìm thấy ngay cả bụi

new RegExp(myRgx).exec(someString); 
Các vấn đề liên quan