2012-11-30 35 views
7

Tôi đang cố gắng để phù hợp với một số văn bản nếu nó không có một khối văn bản trong vùng lân cận của nó. Ví dụ: tôi muốn đối sánh "bar" nếu "foo" không đứng trước nó. Tôi có thể kết hợp "bar" nếu "foo" không ngay trước nó sử dụng cái nhìn tiêu cực đằng sau trong regex này:Regex tiêu cực lookbehinds với một ký tự đại diện

/(?<!foo)bar/ 

nhưng tôi cũng muốn không phù hợp "foo 12345 bar". Tôi đã thử:

/(?<!foo.{1,10})bar/ 

nhưng sử dụng ký tự đại diện + phạm vi có vẻ là một regex không hợp lệ trong Ruby. Tôi có đang nghĩ về vấn đề này không?

Trả lời

9

Bạn đang nghĩ về nó đúng cách. Nhưng tiếc là lookbehinds thường có độ dài cố định. Ngoại lệ chính duy nhất là động cơ regex của .NET, cho phép định lượng lặp lại bên trong lookbehinds. Nhưng vì bạn chỉ cần một cái nhìn tiêu cực và không phải là một lookahead, quá. Có một hack cho bạn. Đảo ngược chuỗi, sau đó cố gắng đối sánh:

/rab(?!.{0,10}oof)/ 

Sau đó đảo ngược kết quả của trận đấu hoặc trừ vị trí phù hợp khỏi độ dài của chuỗi, nếu đó là những gì bạn đang theo dõi.

Bây giờ từ regex bạn đã đưa ra, tôi cho rằng đây chỉ là một phiên bản đơn giản của những gì bạn thực sự cần. Tất nhiên, nếu bar là một mô hình phức tạp, một số suy nghĩ nhiều hơn cần phải đi vào làm thế nào để đảo ngược nó một cách chính xác.

Lưu ý rằng nếu mẫu của bạn yêu cầu cả lookbehinds biến đổi và lookaheads, bạn sẽ có một thời gian khó giải quyết này. Ngoài ra, trong trường hợp của bạn, nó sẽ có thể để mổ xẻ lookbehind của bạn thành nhiều những độ dài thay đổi (vì bạn sử dụng không + cũng không *):

/(?<!foo)(?<!foo.)(?<!foo.{2})(?<!foo.{3})(?<!foo.{4})(?<!foo.{5})(?<!foo.{6})(?<!foo.{7})(?<!foo.{8})(?<!foo.{9})(?<!foo.{10})bar/ 

Nhưng đó không phải là tất cả những gì tốt đẹp, phải không?

+1

Đảo ngược chuỗi là một ý tưởng thú vị. Cảm ơn! –

3

Như m.buettner đã đề cập, lookbehind trong Ruby regex phải có độ dài cố định và được mô tả trong tài liệu. Vì vậy, bạn không thể đặt một định lượng trong một lookbehind.

Bạn không cần phải kiểm tra tất cả trong một bước. Hãy thử thực hiện nhiều bước của regex phù hợp để có được những gì bạn muốn. Giả sử tồn tại của foo trước một trường hợp duy nhất của bar phá vỡ tình trạng bất kể có bar khác, sau đó

string.match(/bar/) and !string.match(/foo.*bar/) 

sẽ cung cấp cho bạn những gì bạn muốn cho ví dụ.

Nếu bạn chứ không phải muốn trận đấu để thành công với bar foo bar, sau đó bạn có thể làm điều này

string.scan(/foo|bar/).first == "bar" 
+0

Đó là vấn đề nếu ý tưởng là để thực sự lấy một trận đấu. Giả sử bạn có 'bar foo bar'. Các regex mà OP đã cố gắng sẽ lấy 'bar' đầu tiên. Giải pháp của bạn sẽ tuyên bố rằng không có kết quả phù hợp. (Ngoài thực tế là bạn đã bỏ qua "tối đa 10 ký tự" heuristic) –

+0

@ m.buettner Bạn và tôi có cách giải thích khác nhau với câu hỏi. – sawa

+1

Chắc chắn. Đó là lý do tại sao tôi không nói giải pháp của bạn là sai.Nhưng tôi thấy điều quan trọng là những giả định và khác biệt như vậy được nêu ra. Bởi vì họ có thể không rõ ràng với OP hoặc bất kỳ ai khác tìm thấy câu hỏi này trong tương lai. –

Các vấn đề liên quan