2011-09-07 11 views
6

Tôi đã xem xét và không thể thực hiện điều này. Tôi không hoàn toàn noob.Regex - Nhận chuỗi giữa hai từ không chứa từ

Tôi cần nhận văn bản được phân cách bởi (bao gồm) START và END không chứa START. Về cơ bản tôi không thể tìm ra cách để phủ nhận toàn bộ từ mà không sử dụng các công cụ nâng cao.

Ví dụ chuỗi:

abcSTARTabcSTARTabcENDabc

Kết quả mong đợi:

STARTabcEND

Không tốt:

0.123.

STARTabcSTARTabcEND

Tôi không thể sử dụng công cụ tìm kiếm lạc hậu. Tôi đang kiểm tra regex của mình tại đây: www.regextester.com

Cảm ơn lời khuyên nào.

+0

Điều gì xảy ra nếu văn bản là 'abcSTARTabcENDabcSTARTabcENDabc'? Bạn có muốn cả hai trận đấu? –

+0

không nghĩ về điều đó ... dù sao, tôi có thể tìm thấy trận đấu thứ hai nếu cần thiết. – rrr

+0

Tốt hơn để làm điều đó trong một regex duy nhất. Tôi đã thêm một câu trả lời. –

Trả lời

4

Giải pháp thực sự cho người đi bộ sẽ là START(([^S]|S*S[^ST]|ST[^A]|STA[^R]|STAR[^T])*(S(T(AR?)?)?)?)END. Các hương vị regex hiện đại có những khẳng định tiêu cực làm điều này thanh lịch hơn, nhưng tôi giải thích nhận xét của bạn về "tìm kiếm ngược" có thể có nghĩa là bạn không thể hoặc không muốn sử dụng tính năng này.

Cập nhật: Chỉ để hoàn thành, lưu ý rằng ở trên là tham lam đối với dấu phân cách cuối. Để chỉ chụp chuỗi ngắn nhất có thể, hãy mở rộng phủ định cũng bao gồm dấu phân cách kết thúc - START(([^ES]|E*E[^ENS]|EN[^DS]|S*S[^STE]|ST[^AE]|STA[^RE]|STAR[^TE])*(S(T(AR?)?)?|EN?)?)END. Tuy nhiên, rủi ro này vượt quá ngưỡng tra tấn trong hầu hết các nền văn hóa.

Sửa lỗi: Một phiên bản trước của câu trả lời này đã có một lỗi, trong SSTART đó có thể là một phần của trận đấu (thứ hai S sẽ phù hợp với [^T], vv).Tôi đã sửa lỗi này nhưng bằng cách thêm S vào [^ST] và thêm S* trước khi không tùy chọn S để cho phép lặp lại tùy ý S nếu không.

+0

Giải pháp tuyệt vời (nếu không thể nhìn thấy được) +1 – stema

+0

+1 để hiển thị cách thực hiện mà không có người nhìn nào – shelleybutterfly

+0

Đây là những gì tôi đang tìm kiếm, cảm ơn. Thật vậy ... người đi bộ :) nhưng nó hoạt động. Tôi đã hy vọng rằng có thể có một cách dễ dàng hơn mà tôi đang mất tích. Xin lỗi vì đã không đăng lại sớm hơn. – rrr

10

Hãy thử điều này

START(?!.*START).*?END 

Xem nó here online on Regexr

(?!.*START) là một lookahead tiêu cực. Nó đảm bảo rằng từ "START" không theo sau

.*? là một kết quả không tham lam của tất cả các ký tự cho đến "END" tiếp theo. Nó cần thiết, bởi vì lookahead tiêu cực chỉ là nhìn về phía trước và không thu bất cứ điều gì (zero chiều dài khẳng định)

Cập nhật:

tôi nghĩ hơn một chút, giải pháp trên là phù hợp cho đến khi "END" đầu tiên. Nếu điều này không được mong muốn (vì bạn không bao gồm START từ nội dung) thì hãy sử dụng phiên bản tham lam

START(?!.*START).*END 

điều này sẽ khớp cho đến khi "END" cuối cùng.

+0

+1 Đánh bại tôi ở đây trước 19 giây. :) –

+0

+1 được thực hiện tốt. –

+0

+1 cho câu trả lời tốt với các giải thích đơn giản của tất cả các toán tử – shelleybutterfly

0

[EDIT: Tôi đã để lại bài đăng này cho thông tin về nhóm chụp nhưng giải pháp chính tôi đưa ra là không chính xác. (?:START)((?:[^S]|S[^T]|ST[^A]|STA[^R]|STAR[^T])*)(?:END) như được chỉ ra trong các nhận xét sẽ không hoạt động; Tôi đã quên rằng các ký tự bỏ qua không thể bị loại bỏ và do đó bạn sẽ cần một cái gì đó như ... |STA(?![^R])| để vẫn cho phép ký tự đó là một phần của END, do đó không thành công trên một cái gì đó như STARTSTAEND; vì vậy nó rõ ràng là một lựa chọn tốt hơn; sau đây sẽ hiển thị đúng cách để sử dụng các nhóm chụp ...]

Câu trả lời được đưa ra bằng cách sử dụng toán tử 'lookahead' không có chiều rộng bằng 0 ''!, với các nhóm chụp, là: (?:START)((?!.*START).*)(?:END). sử dụng $ 1 để thay thế. Nếu bạn muốn bắt các thẻ START và END, bạn có thể thực hiện (START)((?!.*START).*)(END), cung cấp $ 1 = START $ 2 = văn bản và $ 3 = END hoặc các hoán vị khác bằng cách thêm/xóa () s hoặc ?: s.

Bằng cách đó nếu bạn đang sử dụng nó để thực hiện tìm kiếm và thay thế, bạn có thể thực hiện, chẳng hạn như BEGIN $ 1FINISH. Vì vậy, nếu bạn bắt đầu với:

abcSTARTdefSTARTghiENDjkl

bạn sẽ nhận được ghi như chụp nhóm 1, và thay thế bằng BEGIN $ 1FINISH sẽ cung cấp cho bạn như sau:

abcSTARTdefBEGINghiFINISHjkl

mà sẽ cho phép bạn để thay đổi mã thông báo START/END chỉ khi được ghép nối đúng cách.

Mỗi (x) là một nhóm, nhưng tôi đã đặt (?:x) cho mỗi người trong số những người ngoại trừ giữa đánh dấu nó là một nhóm không bắt; người duy nhất tôi rời đi mà không có ?: là giữa; tuy nhiên, bạn cũng có thể nắm bắt được các thẻ BEGIN/END cũng như nếu bạn muốn di chuyển chúng xung quanh hoặc những gì bạn có.

Xem Java regex documentation để biết chi tiết đầy đủ về các regex của Java.

+0

Bạn thất bại trên mẫu STARTSTAEND. – tripleee

+0

@tripleee thở dài, vâng, thực sự và tôi sẽ cần phải bỏ qua những nhân vật với?! mà kinda đánh bại toàn bộ mục đích. Cảm ơn vì chỉ ra điều ấy. – shelleybutterfly

4
START(?:(?!START).)*END 

sẽ hoạt động với số lượng START...END đôi. Để chứng minh bằng Python:

>>> import re 
>>> a = "abcSTARTdefENDghiSTARTjlkENDopqSTARTrstSTARTuvwENDxyz" 
>>> re.findall(r"START(?:(?!START).)*END", a) 
['STARTdefEND', 'STARTjlkEND', 'STARTuvwEND'] 

Nếu bạn chỉ quan tâm về nội dung giữa STARTEND, sử dụng này:

(?<=START)(?:(?!START).)*(?=END) 

Xem nó ở đây:

>>> re.findall(r"(?<=START)(?:(?!START).)*(?=END)", a) 
['def', 'jlk', 'uvw'] 
+0

Yup, Điều này sẽ làm điều đó. +1 (Mặc dù bạn có thể muốn đề cập đến/sử dụng cờ 's' dot-matches-all.) – ridgerunner

2

Tôi có thể đề nghị một thể cải tiến về giải pháp của Tim Pietzcker? Dường như với tôi rằng START(?:(?!START).)*?END tốt hơn để chỉ bắt được một số START ngay lập tức theo sau là END mà không cần bất kỳ START hoặc END nào ở giữa. Tôi đang sử dụng NET và giải pháp của Tim sẽ phù hợp với một cái gì đó giống như START END END. Ít nhất là trong trường hợp cá nhân của tôi, điều này không được mong muốn.