Câu hỏi Mở rộng cú pháp biểu thức chính quy để nói 'không chứa văn bản XYZ'


Tôi có một ứng dụng mà người dùng có thể chỉ định cụm từ thông dụng ở một số địa điểm. Chúng được sử dụng khi chạy ứng dụng để kiểm tra xem văn bản (ví dụ: URL và HTML) có khớp với các regex hay không. Thường thì người dùng muốn có thể nói nơi văn bản khớp với ABC và không khớp với XYZ. Để làm cho nó dễ dàng cho họ để làm điều này tôi đang nghĩ đến việc mở rộng cú pháp biểu thức chính quy trong ứng dụng của tôi với một cách để nói 'và không chứa mẫu


12
2018-05-03 11:04


gốc


Tôi sẽ nói rằng như một người sử dụng bình thường của biểu thức thông thường, không có gì kích thích tôi nhiều hơn là phải tìm hiểu một hương vị đặc biệt, với các tính năng mà tôi không thể sử dụng trong các công cụ regex khác. (Mặc dù thừa nhận rằng người dùng của bạn không phải là tôi, vì vậy có lẽ họ có các tùy chọn khác.) - Charlie Kilian
Chỉ trong trường hợp này là ASP.NET, bạn có thể muốn cẩn thận các cuộc tấn công DoS. Regexes có thể được xây dựng để gây tải máy chủ rất cao. - Justin Morgan
Nó đang sử dụng ¬ nhân vật có hiệu quả giống như cung cấp hai cụm từ thông dụng với dấu tách? Bạn cũng có thể cung cấp bất kỳ số biểu thức chính quy nào trong cùng một cấu hình: pattern1patternt2patternt3negative1negative2. Tất nhiên, hầu hết các hương vị có đủ các mẫu cho điều đó, như các câu trả lời cho thấy. - Kobi
@Justin - Hãy nhớ rằng "người dùng" là quản trị viên. Chủ yếu, họ không được coi là thù địch. - Kobi
@Kobi - Đó là công bằng, mặc dù nó chắc chắn có thể làm điều đó một cách tình cờ. Nếu họ không đủ thông thạo regex để tự viết, họ sẽ không thể điều chỉnh nó. Dù bằng cách nào, chỉ cần một cái gì đó để ghi nhớ. Tôi thích nói dối về mặt thận trọng. - Justin Morgan


Các câu trả lời:


Bạn không cần phải giới thiệu một biểu tượng mới. Đã có hỗ trợ cho những gì bạn cần trong hầu hết các động cơ regex. Nó chỉ là một vấn đề của việc học nó và áp dụng nó.

Bạn có lo ngại về hiệu suất, nhưng bạn đã thử nghiệm nó chưa? Bạn đã đo lường và chứng minh những vấn đề hiệu suất đó chưa? Nó có lẽ sẽ ổn thôi.

Regex làm việc cho nhiều người, trong nhiều tình huống khác nhau. Nó có thể phù hợp với yêu cầu của bạn, quá.

Ngoài ra, regex phức tạp mà bạn tìm thấy trên câu hỏi SO khác, có thể được đơn giản hóa. Có những biểu thức đơn giản cho những cái nhìn tiêu cực và tích cực và trông ngoài.
?!  ?<!  ?=  ?<=


Vài ví dụ

Giả sử văn bản mẫu là <tr valign='top'><td>Albatross</td></tr>

Với các regex sau đây, đây là những kết quả bạn sẽ thấy:

  1. tr       - - trận đấu
  2. td       - - trận đấu
  3. ^td      - - không có trận đấu
  4. ^tr      - - không có trận đấu 
  5. ^<tr     - - trận đấu
  6. ^<tr>.*</tr>  - - không có trận đấu
  7. ^<tr.*>.*</tr>  - - trận đấu
  8. ^<tr.*>.*</tr>(?<tr>)  - - trận đấu
  9. ^<tr.*>.*</tr>(?<!tr>)  - - không có trận đấu
  10. ^<tr.*>.*</tr>(?<!Albatross)  - - trận đấu
  11. ^<tr.*>.*</tr>(?<!.*Albatross.*)  - - không có trận đấu
  12. ^(?!.*Albatross.*)<tr.*>.*</tr> - - không có trận đấu

Giải thích 

Hai trận đấu đầu tiên vì regex có thể áp dụng ở bất kỳ đâu trong chuỗi mẫu (hoặc kiểm thử). Hai thứ hai không khớp, bởi vì ^ nói "bắt đầu từ đầu", và chuỗi thử nghiệm không bắt đầu bằng td hoặc tr - nó bắt đầu bằng một khung góc trái.

Ví dụ thứ năm phù hợp bởi vì chuỗi thử nghiệm bắt đầu bằng <tr. Thứ sáu không, bởi vì nó muốn chuỗi mẫu bắt đầu bằng <tr>, với một khung góc đóng ngay sau tr, nhưng trong chuỗi thử nghiệm thực tế, mở tr bao gồm valign thuộc tính, vì vậy những gì sau tr là một không gian. Regex thứ 7 cho thấy cách cho phép không gian và thuộc tính với các ký tự đại diện.

Regex thứ 8 áp dụng xác nhận tích cực cho sự kết thúc của regex, sử dụng ?<. Nó cho biết, chỉ khớp toàn bộ regex nếu những gì ngay trước con trỏ trong chuỗi thử nghiệm, khớp với những gì trong dấu ngoặc đơn, theo sau ?<. Trong trường hợp này, những gì sau đó là tr>. Sau khi đánh giá `` ^. *, the cursor in the test string is positioned at the end of the test string. Therefore, thetr> `được so khớp với kết thúc của chuỗi kiểm tra, mà đánh giá là TRUE. Do đó, lookbehind tích cực được đánh giá là true, do đó regex tổng thể phù hợp.

Ví dụ thứ chín cho thấy cách chèn xác nhận lookbehind phủ định, sử dụng ?<! . Về cơ bản nó nói "cho phép regex để phù hợp nếu những gì ngay phía sau con trỏ tại thời điểm này, không khớp với những gì sau ?<! trong parens, trong trường hợp này là tr>. Bit của regex trước khẳng định, ^<tr.*>.*</tr> khớp với và bao gồm phần cuối của chuỗi. Bởi vì mô hình tr>  làm khớp với phần cuối của chuỗi. Nhưng đây là một khẳng định tiêu cực, do đó nó đánh giá thành FALSE, có nghĩa là ví dụ thứ 9 KHÔNG phải là một kết quả phù hợp.

Ví dụ thứ mười sử dụng một xác nhận lookbehind tiêu cực khác. Về cơ bản nó nói "cho phép regex để phù hợp nếu những gì ngay phía sau con trỏ tại thời điểm này, không khớp với những gì trong parens, trong trường hợp này Albatross. Bit của regex trước khẳng định, ^<tr.*>.*</tr> khớp với và bao gồm phần cuối của chuỗi. Kiểm tra "Albatross" so với kết thúc của chuỗi mang lại kết quả phủ định, bởi vì chuỗi kiểm tra kết thúc bằng </tr>. Bởi vì các mẫu bên trong các parens của lookbehind tiêu cực không phù hợp, điều đó có nghĩa là lookbehind tiêu cực đánh giá để TRUE, có nghĩa là ví dụ thứ 10 là một trận đấu.

Ví dụ thứ 11 mở rộng giao diện tiêu cực để bao gồm các ký tự đại diện; trong tiếng Anh kết quả của lookbehind tiêu cực là "chỉ khớp nếu chuỗi trước không bao gồm từ AlbatrossTrong trường hợp này chuỗi kiểm tra DOES bao gồm từ, cái nhìn tiêu cực đánh giá là FALSE, và regex thứ 11 không khớp.

Ví dụ thứ 12 sử dụng xác nhận lookahead phủ định. Giống như lookbehinds, lookaheads là zero-width - chúng không di chuyển con trỏ trong chuỗi kiểm tra cho các mục đích của chuỗi phù hợp. Các lookahead trong trường hợp này, từ chối chuỗi ngay lập tức, bởi vì .*Albatross.* kết quả phù hợp; bởi vì nó là một lookahead tiêu cực, nó đánh giá thành FALSE, có nghĩa là regex tổng thể không phù hợp, có nghĩa là đánh giá của regex đối với chuỗi kiểm tra dừng lại ở đó.

ví dụ 12 luôn luôn ước lượng giá trị boolean giống như ví dụ 11, nhưng nó hoạt động khác nhau trong thời gian chạy. Trong ví dụ 12, kiểm tra âm được thực hiện trước, tại các điểm dừng ngay lập tức. Trong ex 11, regex đầy đủ được áp dụng và đánh giá là TRUE, trước khi xác nhận lookbehind được chọn. Vì vậy, bạn có thể thấy rằng có thể có sự khác biệt về hiệu suất khi so sánh lookaheads và lookbehinds. Điều nào phù hợp với bạn tùy thuộc vào những gì bạn đang đối sánh và độ phức tạp tương đối của mẫu "đối sánh dương" và mẫu "đối sánh phủ định".

Để biết thêm về nội dung này, hãy đọc tại http://www.regular-expressions.info/

Hoặc có được một công cụ đánh giá regex và thử một số xét nghiệm.

như công cụ này:
enter image description here

nguồn và nhị phân 


18
2018-05-03 12:43



Cảm ơn bài đăng lớn! Khi tôi thử ví dụ thứ 11, mà theo mô tả của bạn là những gì tôi muốn, như bạn nói nó không phù hợp <tr valign='top'><td>Albatross</td></tr> NHƯNG cũng không khớp <tr valign='top'><td>Albat XXX ross</td></tr>. Tôi đoán điều này là bởi vì cái nhìn tiêu cực chỉ tìm kiếm 'Albatross' sau văn bản phù hợp, không phải bất kỳ nơi nào trong văn bản mẫu? Nếu vậy, làm thế nào tôi có thể thay đổi regex để mô tả là "chỉ phù hợp nếu văn bản mẫu phù hợp ^<tr.*>.*</tr> và toàn bộ văn bản mẫu không bao gồm từ Albatross " - Rory
Bình luận trước đó của tôi là trước khi tôi nhận thấy ví dụ 12. Điều này dường như làm những gì tôi muốn. Tôi nghĩ rằng 12 đưa ra kết quả khác nhau cho ví dụ 11 nếu 'Albatross' được tìm thấy trong mô hình không phải là một phần của lookbehind. - Rory
Điều đó thật tuyệt. Tôi rất vui vì tôi đã đề xuất một giải pháp poxy để gợi ra những câu trả lời tuyệt vời này - cảm ơn rất nhiều! - Rory
Vui mừng được giúp đỡ, Rory. Về nhận xét của bạn cho ex11, trái với những gì bạn báo cáo, tôi nhận được một kết quả tích cực với regex từ 11, bằng cách sử dụng chuỗi kiểm tra <tr valign='top'><td>Albat XXX ross</td></tr> . Đây là những gì tôi mong đợi, vì regex cho 11 có một cái nhìn tiêu cực khẳng định rằng "Albatross" KHÔNG ở trong văn bản trước, ở bất cứ đâu. Xác nhận này là đúng, do đó regex phù hợp. Có vẻ như bạn đang báo cáo điều gì đó khác. Tôi sẽ kiểm tra mã của bạn trên đó. Bạn đã đề cập đến một "lookahead tiêu cực", nhưng ex11 không có điều đó. Nó có âm nhìn về phía sau, và ... (xem tiếp theo) - Cheeso
... để trả lời câu hỏi của bạn, KHÔNG, lookbehind tiêu cực kiểm tra ngược, thậm chí thông qua văn bản phù hợp (và có thể bị bắt). Vì vậy, cái gì khác là askew trong thử nghiệm của bạn. Regexi cho ex11 và ex12 sẽ cho kết quả tương tự, đối với tất cả các chuỗi thử nghiệm. - Cheeso


Bạn có thể dễ dàng hoàn thành mục tiêu của mình bằng cách sử dụng một regex đơn. Đây là một ví dụ minh họa một cách để làm điều đó. Regex này khớp với một chuỗi chứa "cat" VÀ "lion" VÀ "tiger", nhưng KHÔNG chứa "dog" HOẶC LÀ "wolf" HOẶC LÀ "hyena":

if (Regex.IsMatch(text, @"
    # Match string containing all of one set of words but none of another.
    ^                # anchor to start of string.
    # Positive look ahead assertions for required substrings.
    (?=.*?  cat   )  # Assert string has: 'cat'.
    (?=.*?  lion  )  # Assert string has: 'lion'.
    (?=.*?  tiger )  # Assert string has: 'tiger'.
    # Negative look ahead assertions for not-allowed substrings.
    (?!.*?  dog   )  # Assert string does not have: 'dog'.
    (?!.*?  wolf  )  # Assert string does not have: 'wolf'.
    (?!.*?  hyena )  # Assert string does not have: 'hyena'.
    ",
    RegexOptions.Singleline | RegexOptions.IgnoreCase |
    RegexOptions.IgnorePatternWhitespace)) {
    // Successful match
} else {
    // Match attempt failed
} 

Bạn có thể thấy mẫu cần thiết. Khi lắp ráp các regex, hãy chắc chắn để chạy mỗi người dùng cung cấp các chuỗi con thông qua Regex.escape () phương pháp để thoát khỏi bất kỳ metacharacters nó có thể chứa (tức là (, ), | v.v.) Ngoài ra, regex ở trên được viết ở chế độ khoảng trống miễn phí để dễ đọc. Regex sản xuất của bạn nên KHÔNG PHẢI sử dụng chế độ này, nếu không khoảng trắng bên trong các dữ liệu người dùng sẽ bị bỏ qua.

Bạn có thể muốn thêm \btừ ranh giới trước và sau mỗi "từ" trong mỗi khẳng định nếu các chất nền chỉ chứa các từ thực.

Cũng lưu ý rằng xác nhận tiêu cực có thể được thực hiện hiệu quả hơn một chút bằng cách sử dụng cú pháp thay thế sau đây:

(?!.*?(?:dog|wolf|hyena))


9
2018-05-03 15:43



Điều đó thật tuyệt. Tôi rất vui vì tôi đã đề xuất một giải pháp poxy để gợi ra những câu trả lời tuyệt vời này - cảm ơn rất nhiều! Trong quá trình thử nghiệm nhanh chóng của tôi với gskinner.com/RegExr Tôi cần phải thêm nội dung nào đó vào mẫu để khớp với ít nhất một ký tự, ví dụ: đặt . ở cuối mẫu. Tôi chưa kiểm tra nếu điều này là cần thiết với .net regex. - Rory
@Rory - RegExr có vấn đề hiển thị các mẫu có chiều rộng bằng 0 (cho thấy chúng có màu đỏ), bạn thường không cần dấu chấm đó trên bất kỳ hương vị nào, bạn sẽ nhận được một kết quả tích cực theo một trong hai cách. - Kobi