Câu hỏi Làm cách nào (* SKIP) hoặc (* F) hoạt động trên regex?


Tôi đang học cách sử dụng nâng cao của regex và nhận thấy rằng nhiều bài đăng sử dụng (*SKIP) hoặc là (*F) trong đó.

Tôi đã đăng câu hỏi trong đó ý tưởng phù hợp với các dòng không có yellow Nhưng có blue chỉ nếu brown tồn tại sau màu xanh lam. Và câu trả lời đúng là:

.*yellow.*(*SKIP)(*F)|^.*\bblue\b(?=.*brown).*$

Tôi cũng đã thử các biểu thức trông giống như dưới đây nhưng chưa làm việc cho tất cả các trường hợp:

^((?!yellow).)*blue(?=.*brown).*$

Tôi không biết về những (*SKIP)(*F) cờ, do đó, câu hỏi là, các cờ này hoạt động như thế nào? Họ làm gì? Và có những lá cờ khác như thế này không?

Cảm ơn.


22
2017-07-02 15:11


gốc


@SotiriosDelimanolis thực sự tôi đã không sử dụng nó trên mẫu java tôi đang thử nghiệm nó trên regex101 - Federico Piazza
@SotiriosDelimanolis có, bạn là đúng, nhưng ý tưởng là để thực hiện regex này trên java sau này. Đầu tiên tôi muốn học cách regex. Btw, bạn khuyên bạn nên sử dụng thẻ nào thay vì java vì tôi đang dùng nó trên regex101? - Federico Piazza
@ TheLostMind không cần phải xin lỗi, tôi không muốn gây nhầm lẫn cho bất kỳ ai. Cảm ơn bạn đã sửa chữa. - Federico Piazza
Những lá cờ này là một tính năng của Perl Tương thích Regular Expressions (PCRE) vì vậy tôi sẽ khuyên bạn nên đọc nó tài liệu (tìm kiếm các thẻ bạn muốn biết trong tài liệu). Để sử dụng chúng, bạn sẽ cần phải tìm một thư viện regex hỗ trợ chúng cho ngôn ngữ bạn chọn. Tôi không biết bất kỳ thư viện nào như vậy cho Java. - SamYonnou
@Fede: Tôi không nghĩ (*SKIP)(*F) sẽ hoạt động trên Java. Có những cách hack khác trong Java để có được xung quanh biến đổi chiều dài lookbehind trong Java mặc dù. - anubhava


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


Hai động từ kiểm soát ngược này chỉ được thực hiện trong Perl, PCRE và pypi regex mô-đun.

Ý tưởng của (*SKIP)(*FAIL) lừa là tiêu thụ các nhân vật mà bạn muốn tránh, và đó không phải là một phần của kết quả trận đấu.

Một mô hình cổ điển sử dụng thủ thuật này trông giống như sau:

What_I_want_to_avoid(*SKIP)(*FAIL)|What_I_want_to_match

Một công cụ regex xử lý một chuỗi như sau:

  • mã thông báo đầu tiên của mẫu được kiểm tra trên mỗi ký tự từ trái sang phải (theo mặc định phần lớn thời gian, nhưng một số công cụ regex có thể được thiết lập để làm việc từ phải sang trái, .net có thể làm điều này nếu tôi nhớ rõ)

  • nếu mã thông báo đầu tiên khớp với nhau, thì công cụ regex sẽ kiểm tra mã thông báo tiếp theo của mẫu bằng các ký tự tiếp theo (sau lần khớp mã thông báo đầu tiên) v.v.

  • khi mã thông báo không thành công, công cụ regex sẽ nhận được các ký tự khớp với mã thông báo cuối cùng và thử một cách khác để tạo thành công mẫu (nếu nó không hoạt động, động cơ regex sẽ làm tương tự với mã thông báo trước, v.v.)

Khi động cơ regex đáp ứng (*SKIP) động từ (trong trường hợp này tất cả các mã thông báo trước đó rõ ràng đã thành công), nó không còn quyền quay lại tất cả các mã thông báo trước đó ở bên trái và không còn quyền thử lại tất cả các ký tự trùng khớp với một nhánh khác của mẫu hoặc ở vị trí tiếp theo trong chuỗi cho đến ký tự được kết hợp cuối cùng (bao gồm) nếu mẫu không thành công ở bên phải của (*SKIP) động từ.

Vai trò của (*FAIL) là buộc mô hình thất bại. Vì vậy, tất cả các ký tự xuất hiện ở bên trái của (*SKIP) bị bỏ qua và công cụ regex tiếp tục công việc của mình sau những nhân vật này.

Khả năng duy nhất để mô hình thành công trong mẫu ví dụ là nhánh đầu tiên bị lỗi trước (*SKIP) để cho phép nhánh thứ hai được kiểm tra.

Bạn có thể tìm thấy một loại giải thích khác đây.

Về java    và các công cụ regex khác không có hai tính năng này

Động từ kiểm soát ngược không được thực hiện trong các công cụ regex khác và không có tương đương.

Tuy nhiên, bạn có thể sử dụng một số cách để thực hiện tương tự (để rõ ràng hơn, để tránh một cái gì đó mà có thể được kết hợp bởi một phần khác của mẫu).

Việc sử dụng các nhóm chụp:

cách 1:

What_I_want_to_avoid|(What_I_want_to_match)

Bạn chỉ cần trích xuất nhóm chụp 1 (hoặc để kiểm tra nếu nó tồn tại), vì đó là những gì bạn đang tìm kiếm. Nếu bạn sử dụng mẫu để thực hiện thay thế, bạn có thể sử dụng các thuộc tính của kết quả khớp (bù đắp, chiều dài, nhóm chụp) để thực hiện thay thế bằng các hàm chuỗi cổ điển. Ngôn ngữ khác như javascript, ruby ​​... cho phép sử dụng chức năng gọi lại để thay thế.

cách 2:

((?>To_avoid|Other_things_that_can_be_before_what_i_want)*)(What_I_want)

Đó là cách dễ dàng hơn để thay thế, không cần chức năng gọi lại, chuỗi thay thế chỉ cần bắt đầu bằng \1  (hoặc là $1)

Việc sử dụng cách giải quyết:

ví dụ, bạn muốn tìm một từ không được nhúng giữa hai từ khác (cho phép nói S_word và E_word khác nhau (xem bình luận Qtax)):

(các trường hợp cạnh S_word E_word word E_word và S_word word S_word E_word được cho phép trong ví dụ này.)

Cách điều khiển ngược động từ sẽ là:

S_word not_S_word_or_E_word E_word(*SKIP)(*F)|word

Để sử dụng theo cách này, động cơ regex cần cho phép các điều chỉnh độ dài biến đổi ở một mức độ nhất định. Với .net hoặc mô-đun regex mới, không có vấn đề, lookbehinds có thể có một chiều dài hoàn toàn biến. Cũng có thể với Java nhưng kích thước phải được giới hạn (thí dụ: (?<=.{1,1000})).

Tương đương Java sẽ là:

word(?:(?!not_S_word_or_E_word E_word)|(?<!S_word not_E_word{0,1000} word))

Lưu ý rằng trong một số trường hợp, chỉ cần nhìn bề ngoài là cần thiết. Lưu ý rằng việc bắt đầu một mẫu với ký tự chữ là hiệu quả hơn là bắt đầu với một lookbehind, đó là lý do tại sao tôi putted nó sau khi từ (ngay cả khi tôi cần phải viết lại từ một lần nữa trong xác nhận.)


42
2017-07-02 16:03



Giải thích tuyệt vời. Đó là lời giải thích mà tôi đang tìm kiếm. Tôi đang tìm những hành vi này trong java vì vậy tôi nghĩ rằng tôi sẽ đăng một câu hỏi khác. - Federico Piazza
@Fede: Tôi sẽ thêm một bổ sung về Java không có các tính năng này. - Casimir et Hippolyte
Cảm ơn. điều này rất hữu ích, đó là một điều đáng tiếc là động cơ của java không hỗ trợ điều này, nó rất tiện dụng - Federico Piazza
Câu trả lời như thế này làm cho stackoverflow tuyệt vời như vậy !!! Rất cảm ơn rất nhiều. - boulder_02
"Việc sử dụng các cách giải thích" ví dụ không hoàn toàn chính xác, biểu thức lookahead không khớp với các chuỗi giống như (*SKIP)(*F) biểu hiện, cũng không làm những gì văn bản ví dụ nói. Ví dụ word trong chuỗi word E_word phải khớp, nhưng biểu thức lookahead không khớp với nó. - Qtax


Các (*SKIP) và (*F) (aka *FAIL) các mẫu được ghi lại trong sách hướng dẫn Perl: http://perldoc.perl.org/perlre.html 

Tuy nhiên, chúng chỉ có sẵn trong Perl và trong các hương vị của regex bắt chước Perl (ví dụ thư viện PCRE được PHP sử dụng).

Java được xây dựng trong công cụ regex không hỗ trợ các phần mở rộng này, và tôi không nhận thức được một trong đó.

Lời khuyên chung của tôi trong Java là giữ cho các biểu thức chính quy của bạn đơn giản và sử dụng các phương thức thao tác chuỗi khác để đạt được những gì không thể được thực hiện rõ ràng với một regex ngắn.


5
2017-07-02 15:36