a

Câu hỏi Cụm từ thông dụng để khớp với một dòng không chứa một từ?


Tôi biết có thể kết hợp một từ và sau đó đảo ngược các kết quả phù hợp bằng các công cụ khác (ví dụ: grep -v). Tuy nhiên, tôi muốn biết liệu có thể khớp các dòng đó không không chứa một từ cụ thể (ví dụ: hede) sử dụng cụm từ thông dụng.

Đầu vào:

hoho
hihi
haha
hede

Mã số:

grep "<Regex for 'doesn't contain hede'>" input

Kết quả mong muốn:

hoho
hihi
haha

3567


gốc


Có lẽ là một vài năm muộn, nhưng có gì sai với: ([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*? Ý tưởng rất đơn giản. Giữ cho phù hợp cho đến khi bạn thấy sự bắt đầu của chuỗi không mong muốn, sau đó chỉ phù hợp trong trường hợp N-1 nơi chuỗi chưa hoàn thành (trong đó N là độ dài của chuỗi). Những trường hợp N-1 này là "h theo sau là non-e", "anh ta theo sau là non-d" và "hed theo sau là non-e". Nếu bạn quản lý để vượt qua các trường hợp N-1 này, bạn đã thành công không khớp với chuỗi không mong muốn để bạn có thể bắt đầu tìm kiếm [^h]* lần nữa - stevendesu
@stevendesu: hãy thử điều này cho 'một từ rất-rất-dài' hoặc thậm chí tốt hơn một nửa câu. Vui chơi đánh máy. BTW, nó gần như không đọc được. Không biết về tác động hiệu suất. - Peter Schuetze
@PeterSchuetze: Chắc chắn nó không đẹp cho những từ rất dài, nhưng nó là một giải pháp khả thi và chính xác. Mặc dù tôi đã không chạy thử nghiệm về hiệu suất, tôi sẽ không tưởng tượng nó là quá chậm vì hầu hết các quy tắc sau được bỏ qua cho đến khi bạn nhìn thấy một h (hoặc chữ cái đầu tiên của từ, câu, vv). Và bạn có thể dễ dàng tạo chuỗi regex cho các chuỗi dài bằng cách sử dụng nối lặp lặp lại. Nếu nó hoạt động và có thể được tạo ra một cách nhanh chóng, thì tính dễ đọc có quan trọng không? Đó là ý kiến ​​của những gì. - stevendesu
@stevendesu: Tôi thậm chí còn muộn hơn, nhưng câu trả lời đó gần như hoàn toàn sai. cho một điều, nó đòi hỏi đối tượng phải chứa "h" mà nó không cần phải có, với nhiệm vụ là "các dòng khớp [do] không chứa một từ cụ thể". chúng ta hãy giả sử bạn có ý định làm cho nhóm bên trong tùy chọn, và rằng khuôn mẫu được neo: ^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$  điều này không thành công khi các cá thể của "hede" được bắt đầu bằng một phần các cá thể "hede" như trong "hhede". - jaytea
Câu hỏi này đã được thêm vào Câu hỏi thường gặp về biểu hiện cụm từ tràn ngăn xếp, trong "Advanced Regex-Fu". - aliteralmind


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


Khái niệm rằng regex không hỗ trợ kết hợp nghịch đảo không hoàn toàn đúng. Bạn có thể bắt chước hành vi này bằng cách sử dụng các giao diện phủ định:

^((?!hede).)*$

Regex ở trên sẽ khớp với bất kỳ chuỗi hoặc dòng nào không có ngắt dòng, không phải chứa chuỗi (sub) 'hede'. Như đã đề cập, đây không phải là một cái gì đó regex là "tốt" tại (hoặc nên làm), nhưng vẫn còn, nó  khả thi.

Và nếu bạn cần phải khớp các ký tự ngắt dòng, hãy sử dụng DOT-ALL công cụ sửa đổi (dấu sau s trong mẫu sau):

/^((?!hede).)*$/s

hoặc sử dụng nội tuyến:

/(?s)^((?!hede).)*$/

(ở đâu /.../ là các ký tự phân cách regex, tức là không phải là một phần của mẫu)

Nếu bộ sửa đổi DOT-ALL không có sẵn, bạn có thể bắt chước hành vi tương tự với lớp nhân vật [\s\S]:

/^((?!hede)[\s\S])*$/

Giải trình

Một chuỗi chỉ là một danh sách n nhân vật. Trước và sau mỗi ký tự, có một chuỗi rỗng. Vì vậy, một danh sách n nhân vật sẽ có n+1 các chuỗi rỗng. Xem xét chuỗi "ABhedeCD":

    ┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐
S = │e1│ A │e2│ B │e3│ h │e4│ e │e5│ d │e6│ e │e7│ C │e8│ D │e9│
    └──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘

index    0      1      2      3      4      5      6      7

ở đâu elà những chuỗi rỗng. Regex (?!hede). nhìn về phía trước để xem nếu không có chuỗi con "hede" để được nhìn thấy, và nếu đó là trường hợp (vì vậy cái gì khác được nhìn thấy), sau đó . (dấu chấm) sẽ khớp với bất kỳ ký tự nào ngoại trừ ngắt dòng. Nhìn xung quanh cũng được gọi là không xác định chiều rộng bởi vì họ không tiêu thụ bất kỳ nhân vật nào. Họ chỉ xác nhận / xác nhận điều gì đó.

Vì vậy, trong ví dụ của tôi, mọi chuỗi trống được xác thực đầu tiên để xem có không "hede" phía trước, trước khi một nhân vật được tiêu thụ bởi . (dấu chấm). Regex (?!hede). sẽ làm điều đó chỉ một lần, vì vậy nó được bọc trong một nhóm, và lặp đi lặp lại không hoặc nhiều lần: ((?!hede).)*. Cuối cùng, đầu và cuối đầu vào được neo để đảm bảo toàn bộ đầu vào được tiêu thụ: ^((?!hede).)*$

Như bạn thấy, đầu vào "ABhedeCD" sẽ thất bại vì vào e3, regex (?!hede) thất bại (ở đó   "hede" lên phía trước!).


4859



Tôi sẽ không đi xa như vậy để nói rằng đây là một cái gì đó regex là xấu tại. Sự tiện lợi của giải pháp này là khá rõ ràng và hiệu suất hit so với một tìm kiếm có lập trình thường sẽ không quan trọng. - Archimaredes
Nghiêm túc nói tiêu cực loook-trước làm cho bạn biểu hiện thường xuyên không thường xuyên. - Peter K
@PeterK, chắc chắn, nhưng đây là SO, không phải MathOverflow hoặc CS-Stackexchange. Mọi người hỏi một câu hỏi ở đây thường tìm kiếm một câu trả lời thực tế. Hầu hết các thư viện hoặc công cụ (như grep, trong đó OP đề cập đến) với sự hỗ trợ regex tất cả đều có các tính năng mke chúng không thường xuyên theo nghĩa lý thuyết. - Bart Kiers
@Bart Kiers, không có hành vi phạm tội để bạn trả lời, chỉ cần lạm dụng thuật ngữ này kích thích tôi một chút. Phần thực sự khó hiểu ở đây là các biểu thức thông thường theo nghĩa hẹp có thể làm được những gì OP muốn, nhưng ngôn ngữ chung để viết chúng không cho phép nó, dẫn đến các cách giải quyết (toán học xấu xí) như look-aheads. Xin vui lòng xem câu trả lời này dưới đây và bình luận của tôi ở đó (về mặt lý thuyết liên kết) cách thích hợp để làm điều đó. Không cần phải nói nó hoạt động nhanh hơn trên đầu vào lớn. - Peter K
Trong trường hợp bạn từng tự hỏi làm thế nào để làm điều này trong vim: ^\(\(hede\)\@!.\)*$ - baldrs


Lưu ý rằng giải pháp cho không làm bắt đầu với "Hede":

^(?!hede).*$

nói chung hiệu quả hơn nhiều so với giải pháp không làm chứa "Hede":

^((?!hede).)*$

Các kiểm tra trước đây chỉ dành cho "hede" ở vị trí đầu tiên của chuỗi đầu vào, thay vì ở mọi vị trí.


606



Cảm ơn, tôi đã sử dụng nó để xác thực rằng chuỗi không chứa dấu chữ số ^ ((?! \ D {5,}).) * - Samih A
^((?!hede).)*$ đã làm việc cho tôi bằng cách sử dụng plugin jQuery DataTable để loại trừ chuỗi khỏi tập dữ liệu - Alex
Xin chào! Tôi không thể soạn không làm kết thúc với "hede" regex. Bạn có thể giúp với nó? - Aleks Ya
@AleksYa: chỉ cần sử dụng phiên bản "chứa" và bao gồm dấu kết thúc vào chuỗi tìm kiếm: thay đổi chuỗi thành "không khớp" từ "hede" thành "hede $" - Nyerguds
@AleksYa: phiên bản không kết thúc có thể được thực hiện bằng cách sử dụng lookbehind tiêu cực như: (.*)(?<!hede)$. Phiên bản @Nyerguds 'sẽ hoạt động tốt nhưng hoàn toàn bỏ lỡ điểm về hiệu suất mà câu trả lời đề cập đến. - thisismydesign


Nếu bạn chỉ sử dụng nó cho grep, bạn có thể sử dụng grep -v hede để có được tất cả các dòng không chứa hede.

ETA Oh, đọc lại câu hỏi, grep -v có lẽ là ý của bạn bằng "tùy chọn công cụ".


165



Mẹo: để lọc dần những gì bạn không muốn: grep -v "hede" | grep -v "hihi" | ... v.v. - Olivier Lalonde
Hoặc chỉ sử dụng một quy trình grep -v -e hede -e hihi -e ... - Olaf Dietsche
Hay là grep -v "hede\|hihi" :) - Putnik
Nếu bạn có nhiều mẫu mà bạn muốn lọc ra, hãy đặt chúng vào một tệp và sử dụng grep -vf pattern_file file - codeforester
Hoặc đơn giản egrep hoặc là grep -Ev "hede|hihi|etc" để tránh trốn thoát vụng về. - Amit Naidu


Câu trả lời:

^((?!hede).)*$

Giải trình:

^sự bắt đầu của chuỗi, ( nhóm và chụp tới \ 1 (0 hoặc nhiều lần hơn (phù hợp với số tiền nhiều nhất có thể)),
(?! nhìn về phía trước để xem có không,

hedechuỗi của bạn,

) kết thúc nhìn về phía trước, . bất kỳ ký tự nào ngoại trừ \ n,
)* end of \ 1 (Lưu ý: bởi vì bạn đang sử dụng một quantifier trên capture này, chỉ có sự lặp lại LAST của pattern đã capture sẽ được lưu trữ trong \ 1)
$ trước một tùy chọn \ n và kết thúc chuỗi


122



tuyệt vời mà làm việc cho tôi trong văn bản tuyệt vời 2 bằng cách sử dụng nhiều từ '^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$' - Damodar Bashyal
@DamodarBashyal Tôi biết tôi khá muộn ở đây, nhưng bạn hoàn toàn có thể loại bỏ nhiệm kỳ thứ hai ở đó và bạn sẽ nhận được kết quả chính xác - forresthopkinsa


Các câu trả lời được đưa ra là hoàn toàn tốt đẹp, chỉ là một điểm học tập:

Cụm từ thông dụng theo nghĩa khoa học máy tính lý thuyết KHÔNG CÓ KHẢ NĂNG làm như thế này Đối với họ, nó phải giống như thế này:

^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

Điều này chỉ thực hiện một trận đấu ĐẦY ĐỦ. Làm điều đó cho các trận đấu phụ thậm chí sẽ khó xử hơn.


90



Điều quan trọng cần lưu ý là điều này chỉ sử dụng các biểu thức chính quy POSIX.2 cơ bản và do đó trong khi terse là dễ di chuyển hơn khi PCRE không có sẵn. - Steve-o
Tôi đồng ý. Nhiều nếu không phải cụm từ thông dụng nhất không phải là ngôn ngữ thông thường và không thể được nhận diện bởi một automata hữu hạn. - ThomasMcLeod
@ThomasMcLeod, Hades32: Có phải nó nằm trong cõi của bất kỳ ngôn ngữ thông thường nào có thể nói ‘không phải’Và‘và' cũng như 'hoặc là'Của một cụm từ như ‘(hede|Hihi)’? (Đây có thể là một câu hỏi cho CS.) - James Haigh
@ JohnAllen: TÔI!!! … Vâng, không phải là thực tế regex nhưng tài liệu tham khảo học thuật, mà cũng liên quan chặt chẽ đến tính phức tạp; PCRE về cơ bản không thể đảm bảo hiệu quả tương tự như biểu thức chính quy POSIX. - James Haigh
Xin lỗi - câu trả lời chỉ không hoạt động, nó sẽ phù hợp với hhehe và thậm chí phù hợp với hehe một phần (nửa thứ hai) - Falco


Nếu bạn muốn thử nghiệm regex chỉ có thất bại nếu toàn bộ chuỗi phù hợp, những điều sau đây sẽ hoạt động:

^(?!hede$).*

ví dụ. - Nếu bạn muốn cho phép tất cả các giá trị ngoại trừ "foo" (nghĩa là "foofoo", "barfoo" và "foobar" sẽ chuyển, nhưng "foo" sẽ không thành công), hãy sử dụng: ^(?!foo$).*

Tất nhiên, nếu bạn đang kiểm tra chính xác bình đẳng, một giải pháp chung tốt hơn trong trường hợp này là để kiểm tra sự bình đẳng chuỗi, tức là

myStr !== 'foo'

Bạn thậm chí có thể đặt sự phủ định ở ngoài các thử nghiệm nếu bạn cần bất kỳ tính năng regex (ở đây, trường hợp không nhạy cảm và phạm vi phù hợp):

!/^[a-f]oo$/i.test(myStr)

Các giải pháp regex ở đầu có thể hữu ích, tuy nhiên, trong những tình huống mà một bài kiểm tra regex tích cực là cần thiết (có lẽ bởi một API).


49



những gì về dấu chấm trắng đuôi? Ví dụ, nếu tôi muốn thử nghiệm thất bại với chuỗi " hede "? - eagor
@eagor the \s chỉ thị khớp với một ký tự khoảng trống duy nhất - Roy Tinker
cảm ơn, nhưng tôi đã không quản lý để cập nhật regex để thực hiện công việc này. - eagor
@eagor: ^(?!\s*hede\s*$).* - Roy Tinker


Đây là một lời giải thích tốt lý do tại sao nó không dễ dàng phủ nhận một regex tùy ý. Tôi phải đồng ý với các câu trả lời khác, mặc dù: nếu đây là bất cứ điều gì khác hơn là một câu hỏi giả định, sau đó một regex không phải là sự lựa chọn đúng ở đây.


48



Một số công cụ, và đặc biệt là mysqldumpslow, chỉ cung cấp cách lọc dữ liệu, vì vậy trong trường hợp này, việc tìm kiếm một regex để làm điều này là giải pháp tốt nhất ngoài việc viết lại công cụ (các bản vá khác nhau này chưa được bao gồm bởi MySQL AB / Sun / Oracle. - FGM
Chính xác về tình hình của tôi. Công cụ mẫu Velocity sử dụng các biểu thức chính quy để quyết định khi áp dụng một phép biến đổi (thoát html) và tôi muốn nó luôn hoạt động EXCEPT trong một tình huống. - Henno Vermeulen
Có gì thay thế? Ive không bao giờ gặp phải bất cứ điều gì có thể làm khớp chuỗi chính xác bên cạnh regex. Nếu OP đang sử dụng một ngôn ngữ lập trình, có thể có các công cụ khác có sẵn, nhưng nếu anh ta / cô ấy đang sử dụng không viết mã, có lẽ không phải là bất kỳ sự lựa chọn nào khác. - kingfrito_5005
Một trong nhiều kịch bản không giả định trong đó regex là lựa chọn tốt nhất có sẵn: Tôi đang trong một IDE (Android Studio) cho thấy đầu ra nhật ký và các công cụ lọc duy nhất được cung cấp là: các chuỗi đơn giản và regex. Cố gắng làm điều này với các chuỗi đơn giản sẽ là một thất bại hoàn toàn. - LarsH


FWIW, vì ngôn ngữ thông thường (còn gọi là ngôn ngữ hợp lý) được đóng theo bổ sung, nên luôn luôn có thể tìm thấy một biểu thức chính quy (hay còn gọi là biểu thức hợp lý) phủ nhận một biểu thức khác. Nhưng không có nhiều công cụ thực hiện điều này.

Vcsn hỗ trợ toán tử này (nó biểu thị {c}, postfix).

Trước tiên, bạn xác định loại biểu thức của mình: nhãn là chữ cái (lal_char) để chọn từ a đến z ví dụ (xác định bảng chữ cái khi làm việc với bổ sung là, tất nhiên, rất quan trọng), và "giá trị" được tính cho mỗi từ chỉ là một Boolean: true từ được chấp nhận, false, từ chối.

Trong Python:

In [5]: import vcsn
        c = vcsn.context('lal_char(a-z), b')
        c
Out[5]: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z} → 𝔹

sau đó bạn nhập biểu thức của bạn:

In [6]: e = c.expression('(hede){c}'); e
Out[6]: (hede)^c

chuyển đổi biểu thức này thành một automaton:

In [7]: a = e.automaton(); a

The corresponding automaton

cuối cùng, chuyển đổi automaton này trở lại thành một biểu thức đơn giản.

In [8]: print(a.expression())
        \e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]*

Ở đâu + thường được biểu thị |, \e biểu thị từ trống và [^] thường được viết . (bất kỳ nhân vật nào). Vì vậy, với một chút viết lại ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*.

Bạn có thể xem ví dụ này đâyvà thử dùng Vcsn trực tuyến ở đó.


44



Đúng, nhưng xấu xí, và chỉ có thể thực hiện được cho các bộ ký tự nhỏ. Bạn không muốn làm điều này với các chuỗi Unicode :-) - reinierpost
Có nhiều công cụ cho phép nó, một trong những ấn tượng nhất Ragel. Ở đó, nó sẽ được viết thành (bất kỳ * - ('hehe' bất kỳ *)) cho kết quả phù hợp bắt đầu hoặc (bất kỳ * - ('hehe' bất kỳ *)) nào cho chưa được căn chỉnh. - Peter K
@reinierpost: tại sao nó xấu xí và vấn đề với unicode là gì? Tôi không thể đồng ý cả hai. (Tôi không có kinh nghiệm với vcsn, nhưng có với DFA). - Peter K
Regexp ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).* đã không làm việc cho tôi bằng cách sử dụng egrep. Nó phù hợp hede. Tôi cũng đã cố gắng neo nó vào đầu và cuối, và nó vẫn không hoạt động. - Pedro Gimeno
@PedroGimeno Khi bạn neo, bạn đã chắc chắn để đặt regex này trong parens đầu tiên? Nếu không thì các precedences giữa neo và | sẽ không chơi độc đáo. '^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'. - akim