2013-08-14 21 views
11

Trong Perl regexes, các biểu thức như \1, \2, v.v. thường được hiểu là "backreferences" đối với các nhóm đã chụp trước đó, nhưng không phải như vậy khi \1, \2, v.v ... xuất hiện trong lớp nhân vật. Trong trường hợp thứ hai, \ được coi là ký tự thoát (và do đó \1 chỉ là 1, v.v.). Do đó, nếu (ví dụ) muốn khớp một chuỗi (có chiều dài lớn hơn 1) có ký tự đầu tiên khớp với ký tự cuối cùng, nhưng không xuất hiện ở bất kỳ nơi nào khác trong chuỗi, thì regex sau sẽ không :Cách tiếp cận chung cho (tương đương) "backreferences trong lớp nhân vật"?

/\A  # match beginning of string; 
(.)  # match and capture first character (referred to subsequently by \1); 
[^\1]* # (WRONG) match zero or more characters different from character in \1; 
\1  # match \1; 
\z  # match the end of the string; 
/sx  # s: let . match newline; x: ignore whitespace, allow comments 

sẽ không làm việc, kể từ khi nó phù hợp (ví dụ) chuỗi 'a1a2a':

DB<1> ('a1a2a' =~ /\A(.)[^\1]*\1\z/ and print "fail!") or print "success!" 
fail! 

tôi thường có thể quản lý để tìm một số workaround , nhưng nó luôn luôn là vấn đề cụ thể, và thường phức tạp hơn nhiều so với những gì tôi sẽ làm gì nếu tôi có thể sử dụng backreferences trong một lớp nhân vật.

Có cách giải quyết chung (và hy vọng đơn giản) không?


Ví dụ, đối với các vấn đề trong ví dụ trên, tôi muốn sử dụng cái gì đó như

/\A 
(.)    # match and capture first character (referred to subsequently 
        # by \1); 
(?!.*\1\.+\z) # a negative lookahead assertion for "a suffix containing \1"; 
.*    # substring not containing \1 (as guaranteed by the preceding 
        # negative lookahead assertion); 
\1\z    # match last character only if it is equal to the first one 
/sx 

... nơi tôi đã thay thế đơn giản một cách hợp lý (mặc dù, than ôi, không chính xác) subexpression [^\1]* trong regex trước đó với một số xác định rõ ràng hơn là cấm tìm kiếm tiêu cực, xem xét số (?!.*\1.+\z). Xác nhận này về cơ bản nói "từ bỏ nếu \1 xuất hiện ở bất kỳ nơi nào ngoài điểm này (ngoài vị trí cuối cùng)." Ngẫu nhiên, tôi đưa ra giải pháp này chỉ để minh họa cho các loại giải pháp mà tôi đã đề cập đến trong câu hỏi. Tôi không cho rằng đó là một điều đặc biệt tốt.

+0

Giải pháp được chấp nhận là hoàn hảo cho phủ định, nhưng sẽ không bao gồm một số cách sử dụng khác của các lớp ký tự, chẳng hạn như phạm vi. Giả sử bạn muốn khớp tất cả các chuỗi gồm 3 chữ số theo thứ tự không giảm (vì vậy "111", "123", "368", "449", nhưng không phải là "987" hoặc "322"). Sử dụng backrefs trong các lớp ký tự, regex giả sẽ là '/^([0-9]) ([\ 1-9]) ([\ 2-9]) $ /', nhưng bạn không thể thực hiện tương tự đơn giản với một cái nhìn tiêu cực. – rampion

Trả lời

10

Điều này có thể được thực hiện với một lookahead tiêu cực trong một nhóm lặp đi lặp lại:

/\A   # match beginning of string; 
(.)  # match and capture first character (referred to subsequently by \1); 
((?!\1).)* # match zero or more characters different from character in \1; 
\1   # match \1; 
\z   # match the end of the string; 
/sx 

mô hình này có thể được sử dụng ngay cả khi nhóm chứa nhiều hơn một ký tự.

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