2009-10-05 25 views
187

Sự cố với cụm từ thông dụng này khi tôi sử dụng cờ toàn cầu và cờ không phân biệt chữ hoa chữ thường là gì? Truy vấn là đầu vào do người dùng tạo. Kết quả phải là [đúng, đúng].Tại sao RegExp với cờ toàn cầu cho kết quả sai?

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

+36

Chào mừng bạn đến với một trong nhiều bẫy của RegExp trong JavaScript. Nó có một trong những giao diện tồi tệ nhất để xử lý regex mà tôi từng gặp, đầy những tác dụng phụ lạ và tối nghĩa. Hầu hết các tác vụ phổ biến mà bạn thường muốn làm với regex là khó đánh vần đúng. – bobince

+0

XRegExp trông giống như một lựa chọn tốt. http://xregexp.com/ – about

+0

Xem câu trả lời tại đây: http://stackoverflow.com/questions/604860/interesting-test-of-javascript-regexp – Prestaul

Trả lời

245

Đối tượng RegExp theo dõi những lastIndex nơi một trận đấu xảy ra, vì vậy các trận đấu tiếp theo nó sẽ bắt đầu từ chỉ số được sử dụng cuối cùng, thay vì của 0. Hãy xem:

var query = 'Foo B'; 
var re = new RegExp(query, 'gi'); 
var result = []; 
result.push(re.test('Foo Bar')); 

alert(re.lastIndex); 

result.push(re.test('Foo Bar')); 

Nếu bạn không muốn đặt lại thủ công lastIndex thành 0 sau mỗi lần kiểm tra, chỉ cần xóa g cờ.

Dưới đây là các thuật toán mà các thông số kỹ thuật ra lệnh (phần 15.10.6.2): ​​

RegExp.prototype.exec (string)

Thực hiện một biểu thức trận đấu thường xuyên của chuỗi chống lại các biểu thức chính quy và trả về một đối tượng Array chứa kết quả của trận đấu, hoặc null nếu chuỗi không phù hợp với Chuỗi ToString (string) được tìm kiếm một sự xuất hiện của biểu thức chính quy mô hình như sau:

  1. Hãy S là giá trị của ToString (string).
  2. Hãy để độ dài là S.
  3. Hãy để cho LastIndex là giá trị của thuộc tính lastIndex.
  4. Để tôi là giá trị của ToInteger (lastIndex).
  5. Nếu thuộc tính chung là sai, hãy để i = 0.
  6. Nếu tôi < 0 hoặc I> độ dài thì đặt lastIndex thành 0 và trả về giá trị rỗng.
  7. Gọi [[Match]], cho nó đối số S và i. Nếu [[Match]] trả về lỗi, hãy chuyển đến bước 8; nếu không, hãy để r là kết quả Nhà nước và chuyển đến bước 10.
  8. Hãy để i = i + 1.
  9. Chuyển đến bước 6.
  10. Hãy để giá trị endIndex của e be r.
  11. Nếu thuộc tính chung là đúng, hãy đặt lastIndex thành e.
  12. Gọi n là độ dài của mảng chụp của r. (Đây là cùng một giá trị như NCapturingParens 15.10.2.1 của.)
  13. Return một mảng mới với các thuộc tính sau:
    • Chỉ số tài sản được thiết lập để vị trí của chuỗi phù hợp trong hoàn thành chuỗi S.
    • thuộc tính đầu vào được thiết lập để S.
    • thuộc tính chiều dài được thiết lập để n + 1.
    • Các 0 prope rty được đặt thành chuỗi con phù hợp (tức là phần của S giữa bù đắp i bao gồm và bù đắp e độc ​​quyền).
    • Đối với mỗi số số nguyên i sao cho I> 0 và I ≤ n, đặt thuộc tính có tên ToString (i) thành phần tử thứ i của mảng chụp của r.
+39

Điều này giống như Hướng dẫn thiết kế Galaxy API của Hitchhiker tại đây. "Đó là pitfall mà bạn rơi vào đã được hoàn toàn tài liệu trong spec trong vài năm, nếu bạn chỉ có bothered để kiểm tra" – Retsam

+4

Firefox dính cờ không làm những gì bạn ngụ ý ở tất cả. Thay vào đó, nó hoạt động như thể có một^khi bắt đầu biểu thức chính quy, XÁC NHẬN rằng điều này^khớp với vị trí * chuỗi * hiện tại (lastIndex) thay vì bắt đầu chuỗi. Bạn đang thử nghiệm hiệu quả nếu regex khớp với "ngay tại đây" thay vì "ở bất kỳ đâu sau lastIndex". Xem liên kết bạn đã cung cấp! – Doin

+0

Câu mở đầu của câu trả lời này không chính xác. Bạn đã đánh dấu bước 3 của thông số không nói gì. Ảnh hưởng thực tế của 'lastIndex' là trong các bước 5, 6 và 11. Câu lệnh mở đầu của bạn chỉ đúng nếu NÚT TOÀN CẦU được đặt. – Prestaul

59

Bạn đang sử dụng một đối tượng duy nhất RegExp và thực hiện nó nhiều lần. Trên mỗi lần thực thi liên tiếp, nó tiếp tục từ chỉ mục kết hợp cuối cùng.

Bạn cần phải "thiết lập lại" regex để bắt đầu ngay từ đầu trước mỗi hành:

result.push(re.test('Foo Bar')); 
re.lastIndex = 0; 
result.push(re.test('Foo Bar')); 
// result is now [true, true] 

Có nói rằng nó có thể dễ đọc hơn để tạo ra một đối tượng RegExp mới mỗi lần (overhead là tối thiểu là RegExp được cache anyway):

result.push((/Foo B/gi).test(stringA)); 
result.push((/Foo B/gi).test(stringB)); 
32

RegExp.prototype.test cập nhật tài sản lastIndex biểu thức chính quy để mỗi kiểm tra sẽ bắt đầu nơi người cuối cùng dừng lại. Tôi muốn đề nghị sử dụng String.prototype.match vì nó không cập nhật các lastIndex tài sản:

!!'Foo Bar'.match(re); // -> true 
!!'Foo Bar'.match(re); // -> true 

Lưu ý: !! chuyển nó đến một boolean và sau đó sẽ đảo ngược boolean nên nó phản ánh kết quả.

Ngoài ra, bạn chỉ có thể thiết lập lại các lastIndex tài sản:

result.push(re.test('Foo Bar')); 
re.lastIndex = 0; 
result.push(re.test('Foo Bar')); 
9

Loại bỏ toàn cầu g cờ sẽ khắc phục vấn đề của bạn.

var re = new RegExp(query, 'gi'); 

Nên

var re = new RegExp(query, 'i'); 
Các vấn đề liên quan