นิพจน์ปกติเพื่อจับคู่บรรทัดที่ไม่มีคำ


4292

ฉันรู้ว่าเป็นไปได้ที่จะจับคู่คำแล้วย้อนกลับการแข่งขันโดยใช้เครื่องมืออื่น ๆ (เช่นgrep -v) อย่างไรก็ตามเป็นไปได้หรือไม่ที่จะจับคู่บรรทัดที่ไม่มีคำเฉพาะเช่นการhedeใช้นิพจน์ทั่วไป?

การป้อนข้อมูล:

hoho
hihi
haha
hede

รหัส:

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

ผลลัพธ์ที่ต้องการ:

hoho
hihi
haha

84
อาจจะเป็นปีที่สองปลาย แต่สิ่งที่ผิดปกติกับ: ([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*? ความคิดนั้นง่าย คงการจับคู่ไว้จนกระทั่งคุณเห็นจุดเริ่มต้นของสตริงที่ไม่ต้องการจากนั้นจับคู่เฉพาะในกรณี N-1 ที่สตริงยังไม่เสร็จ (โดยที่ N คือความยาวของสตริง) กรณี N-1 เหล่านี้คือ "h ตามด้วย non-e", "เขาตามด้วย non-d" และ "hed ตามด้วย non-e" หากคุณจัดการเพื่อผ่านกรณี N-1 เหล่านี้คุณไม่ประสบความสำเร็จในการจับคู่สตริงที่ไม่พึงประสงค์เพื่อให้คุณสามารถเริ่มต้นค้นหา[^h]*อีกครั้ง
stevendesu

323
@stevendesu: ลองใช้คำว่า 'a-very-long-long-word' หรือดีกว่าครึ่งประโยค ขอให้สนุกกับการพิมพ์ BTW มันเกือบอ่านไม่ได้ ไม่ทราบเกี่ยวกับผลกระทบด้านประสิทธิภาพ
Peter Schuetze

13
@ PeterSchuetze: แน่นอนว่ามันไม่ได้สวยสำหรับคำที่ยาวมาก แต่มันเป็นทางออกที่ทำงานได้และถูกต้อง แม้ว่าฉันจะไม่ได้ทำการทดสอบประสิทธิภาพ แต่ฉันก็ไม่คิดว่ามันช้าเกินไปเนื่องจากกฎส่วนใหญ่จะถูกเพิกเฉยจนกว่าคุณจะเห็น h (หรือตัวอักษรตัวแรกของคำประโยค ฯลฯ ) และคุณสามารถสร้างสตริง regex สำหรับสตริงที่ยาวโดยใช้การต่อข้อมูลแบบวนซ้ำ หากใช้งานได้และสามารถสร้างได้อย่างรวดเร็วความชัดเจนเป็นสิ่งสำคัญหรือไม่ นั่นคือสิ่งที่แสดงความคิดเห็น
stevendesu

57
@stevendesu: ฉันยังใหม่กว่า แต่คำตอบนั้นเกือบผิดทั้งหมด สำหรับสิ่งหนึ่งมันต้องมีหัวเรื่องที่จะมี "h" ซึ่งมันไม่ควรจะต้องเพราะงานคือ "การจับคู่สายที่ [ไม่] ไม่มีคำที่เฉพาะเจาะจง" ให้เราคิดว่าคุณตั้งใจจะทำให้กลุ่มภายในเป็นตัวเลือกและรูปแบบนั้นถูกยึด: ^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$ สิ่งนี้จะล้มเหลวเมื่ออินสแตนซ์ของ "hede" ถูกนำหน้าด้วยอินสแตนซ์บางส่วนของ "hede" เช่นใน "hhede"
jaytea

8
คำถามนี้ถูกเพิ่มไปยังคำถามที่พบบ่อยของสแต็คโอเวอร์โฟลว์นิพจน์ปกติภายใต้ "Advanced Regex-Fu"
aliteralmind

คำตอบ:


5892

ความคิดที่ว่า regex ไม่สนับสนุนการจับคู่ผกผันนั้นไม่เป็นความจริง แต่อย่างใด คุณสามารถเลียนแบบพฤติกรรมนี้ได้โดยใช้การดูเชิงลบ:

^((?!hede).)*$

regex ด้านบนจะจับคู่สตริงใด ๆ หรือบรรทัดที่ไม่มีตัวแบ่งบรรทัดไม่ประกอบด้วยสตริง (ย่อย) 'hede' ดังที่กล่าวมานี้ไม่ใช่สิ่งที่ regex เป็น "ดี" ที่ (หรือควรทำ) แต่ก็ยังเป็นไปได้

และหากคุณต้องการจับคู่ตัวแบ่งบรรทัดด้วยให้ใช้ตัวดัดแปลง DOT-ALL (ตัวต่อท้ายsในรูปแบบต่อไปนี้):

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

หรือใช้แบบอินไลน์:

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

(โดยที่/.../เป็นตัวคั่น regex คือไม่ใช่ส่วนหนึ่งของรูปแบบ)

หากไม่มีตัวปรับ DOT-ALL คุณสามารถเลียนแบบพฤติกรรมเดียวกันกับคลาสอักขระ[\s\S]:

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

คำอธิบาย

สตริงเป็นเพียงรายการของnตัวละคร ก่อนและหลังอักขระแต่ละตัวจะมีสตริงว่าง ดังนั้นรายการของnอักขระจะมีn+1สตริงว่าง พิจารณาสตริง"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

โดยที่eคือสตริงว่าง Regex (?!hede).มองไปข้างหน้าเพื่อดูว่าไม่มีซับสตริง"hede"ที่จะเห็นหรือไม่และถ้าเป็นเช่น.นั้น การมองไปรอบ ๆ นั้นเรียกว่าการยืนยันความกว้างเป็นศูนย์เพราะพวกมันไม่ใช้อักขระใด ๆ พวกเขายืนยัน / ตรวจสอบบางสิ่งเท่านั้น

ในตัวอย่างของฉันสตริงว่างทั้งหมดจะถูกตรวจสอบก่อนเพื่อดูว่าไม่มีอะไร"hede"เกิดขึ้นก่อนที่อักขระ.(dot) จะใช้หมด regex จะทำเพียงครั้งเดียวดังนั้นมันเป็นห่อในกลุ่มและทำซ้ำศูนย์หรือมากกว่าครั้ง:(?!hede). ((?!hede).)*ในที่สุดจุดเริ่มต้นและจุดสิ้นสุดของอินพุตจะถูกยึดเพื่อให้แน่ใจว่ามีการใช้อินพุตทั้งหมด:^((?!hede).)*$

ในขณะที่คุณสามารถดูการป้อนข้อมูลที่"ABhedeCD"จะล้มเหลวเพราะe3, regex ไม่(?!hede)ล้มเหลว (มีเป็น "hede"ขึ้นข้างหน้า!)


26
ฉันจะไม่ไปไกลเท่าที่จะบอกว่านี่คือสิ่งที่ regex ไม่ดีที่ ความสะดวกสบายของโซลูชันนี้ค่อนข้างชัดเจนและประสิทธิภาพที่ได้รับเมื่อเปรียบเทียบกับการค้นหาแบบเป็นโปรแกรมมักจะไม่สำคัญ
Archimaredes

29
การพูดเชิงลบอย่างชัดเจนล่วงหน้าจะทำให้คุณไม่ได้พูดปกติ
Peter K

55
@PeterK แน่นอน แต่นี่คือ SO ไม่ใช่ MathOverflow หรือ CS-Stackexchange คนที่ถามคำถามที่นี่มักจะมองหาคำตอบที่เป็นประโยชน์ ห้องสมุดหรือเครื่องมือส่วนใหญ่ (เช่นgrepที่ OP กล่าวถึง) ด้วยการสนับสนุน regex ทั้งหมดมีคุณสมบัติที่ mke พวกเขาไม่ปกติในแง่ทฤษฎี
บาร์ต Kiers

19
@Bart Kiers ไม่มีความผิดที่คุณจะตอบเพียงแค่การละเมิดคำศัพท์นี้ทำให้ฉันรำคาญเล็กน้อย ส่วนที่สับสนอย่างมากที่นี่คือการแสดงออกปกติในความหมายที่เข้มงวดสามารถทำสิ่งที่ OP ต้องการได้ แต่ภาษาทั่วไปในการเขียนไม่อนุญาตซึ่งจะนำไปสู่การแก้ไขปัญหาทางคณิตศาสตร์ โปรดดูคำตอบนี้ด้านล่างและความคิดเห็นของฉันมีสำหรับ (ชิดในทางทฤษฎี) วิธีที่เหมาะสมในการทำมัน ไม่จำเป็นต้องบอกว่าใช้งานได้เร็วกับอินพุตขนาดใหญ่
Peter K

17
ในกรณีที่คุณเคยสงสัยว่าการทำเช่นนี้ในกลุ่ม:^\(\(hede\)\@!.\)*$
baldrs

738

โปรดทราบว่าโซลูชันที่ไม่ได้ขึ้นต้นด้วย “ hede” :

^(?!hede).*$

โดยทั่วไปจะมีประสิทธิภาพมากขึ้นกว่าวิธีการไม่ได้มี “Hede” :

^((?!hede).)*$

อดีตตรวจสอบ "hede" เฉพาะที่ตำแหน่งแรกของสตริงอินพุตมากกว่าที่ทุกตำแหน่ง


5
ขอบคุณฉันใช้มันเพื่อตรวจสอบความถูกต้องของสตริงที่ไม่ได้มีจำนวนของตัวเลข ^ ((?! \ d {5,}).) *
Samih A

2
สวัสดี! ฉันไม่สามารถเขียนไม่ได้จบด้วย "hede" regex คุณช่วยได้ไหม
Aleks Ya

1
@AleksYa: เพียงแค่ใช้รุ่น "บรรจุ" และรวมถึงจุดยึดท้ายลงในสตริงการค้นหา: เปลี่ยนสตริงเป็น "ไม่ตรง" จาก "hede" เป็น "hede $"
Nyerguds

2
@AleksYa: รุ่นที่ไม่ได้จบที่สามารถทำได้โดยใช้ lookbehind (.*)(?<!hede)$เชิงลบเป็น: รุ่น @Nyerguds 'จะใช้งานได้เช่นกัน แต่พลาดจุดที่แสดงถึงคำตอบอย่างสมบูรณ์
thisismydesign

5
ทำไมคำตอบจำนวนมากบอกว่า^((?!hede).)*$? มันไม่มีประสิทธิภาพในการใช้มากกว่านี้^(?!.*hede).*$เหรอ? มันทำสิ่งเดียวกัน แต่ใช้เวลาน้อยลง
JackPRead

208

หากคุณใช้เพื่อ grep คุณสามารถใช้grep -v hedeเพื่อรับทุกบรรทัดที่ไม่มี hede

การทางพิเศษแห่งประเทศไทยโอ้อ่านคำถามอีกครั้งgrep -vอาจเป็นสิ่งที่คุณหมายถึงโดย "ตัวเลือกเครื่องมือ"


22
เคล็ดลับ: สำหรับการกรองสิ่งที่คุณไม่ต้องการอย่างต่อเนื่อง: grep -v "hede" | grep -v "hihi" | ... ฯลฯ
Olivier Lalonde

51
หรือใช้เพียงกระบวนการเดียวเท่านั้นgrep -v -e hede -e hihi -e ...
Olaf Dietsche

15
หรือแค่grep -v "hede\|hihi":)
Putnik

2
หากคุณมีรูปแบบมากมายที่คุณต้องการกรองเอาไว้ในไฟล์และใช้grep -vf pattern_file file
codeforester

4
หรือเพียงegrepหรือ grep -Ev "hede|hihi|etc"เพื่อหลีกเลี่ยงการหนีที่น่าอึดอัดใจ
Amit Naidu

160

ตอบ:

^((?!hede).)*$

คำอธิบาย:

^จุดเริ่มต้นของสตริง (จัดกลุ่มและจับไปที่ \ 1 (0 หรือมากกว่านั้น (ตรงกับจำนวนที่มากที่สุดเท่าที่เป็นไปได้))
(?!ดูล่วงหน้าเพื่อดูว่าไม่มี

hede สตริงของคุณ

)สิ้นสุดการดูล่วงหน้า .ตัวละครใด ๆ ยกเว้น \ n,
)*จุดสิ้นสุดของ \ 1 (หมายเหตุ: เนื่องจากคุณใช้ตัวระบุปริมาณในการจับภาพนี้จะมีการจัดเก็บการทำซ้ำรูปแบบการจับภาพครั้งสุดท้ายเท่านั้นที่จะถูกเก็บไว้ใน \ 1)
$ก่อนตัวเลือก \ n และจุดสิ้นสุดของสตริง


14
ยอดเยี่ยมที่ทำงานสำหรับฉันในข้อความประเสริฐ 2 โดยใช้คำหลายคำ ' ^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$'
Damodar Bashyal

3
@DamodarBashyal ฉันรู้ว่าฉันค่อนข้างจะสายที่นี่ แต่คุณสามารถลบคำที่สองที่นั่นและคุณจะได้รับผลลัพธ์ที่แน่นอนเหมือนกัน
forresthopkinsa

99

คำตอบที่ได้รับนั้นสมบูรณ์แบบเป็นเพียงจุดทางวิชาการ:

นิพจน์ทั่วไปในความหมายของวิทยาศาสตร์คอมพิวเตอร์เชิงทฤษฎีไม่สามารถทำเช่นนี้ได้ สำหรับพวกมันมันต้องมีหน้าตาแบบนี้:

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

สิ่งนี้จะจับคู่แบบเต็มเท่านั้น ทำเพื่อการแข่งขันย่อยจะยิ่งน่าอึดอัดใจ


1
สิ่งสำคัญที่ควรทราบคือใช้เฉพาะนิพจน์ปกติ POSIX.2 พื้นฐานเท่านั้นและในขณะที่ terse นั้นสามารถพกพาได้มากกว่าเมื่อไม่มี PCRE
Steve-o

5
ฉันเห็นด้วย. นิพจน์จำนวนมากหากไม่ใช่ส่วนใหญ่ไม่ใช่ภาษาปกติและไม่สามารถรับรู้ได้โดยออโต จำกัด
ThomasMcLeod

@ThomasMcLeod, Hades32: มันอยู่ในขอบเขตของภาษาปกติที่เป็นไปได้หรือไม่ที่จะพูดว่า ' ไม่ ' และ ' และ ' และ 'รวมถึง' หรือ 'ของการแสดงออกเช่น' (hede|Hihi)' (นี่อาจเป็นคำถามสำหรับ CS)
James Haigh

7
@JohnAllen: ME !!! …เอ้อไม่ใช่ regex จริงๆ แต่เป็นข้อมูลอ้างอิงทางวิชาการซึ่งสัมพันธ์อย่างใกล้ชิดกับความซับซ้อนในการคำนวณ PCRE พื้นฐานไม่สามารถรับประกันประสิทธิภาพเช่นเดียวกับการแสดงออกปกติ POSIX
James Haigh

4
ขออภัย - คำตอบนี้ใช้งานไม่ได้มันจะจับคู่กับ hhehe และจับคู่กับ hehe บางส่วน (ครึ่งหลัง)
Falco

60

หากคุณต้องการทดสอบ regex ไปเพียงล้มเหลวถ้าสตริงทั้งหมดแมตช์ต่อไปนี้จะทำงาน:

^(?!hede$).*

เช่น - หากคุณต้องการอนุญาตค่าทั้งหมดยกเว้น "foo" (เช่น "foofoo", "barfoo" และ "foobar" จะผ่าน แต่ "foo" จะล้มเหลว) ให้ใช้: ^(?!foo$).*

แน่นอนว่าหากคุณกำลังตรวจสอบความเท่าเทียมกันแน่นอนทางออกทั่วไปที่ดีกว่าในกรณีนี้คือการตรวจสอบความเท่าเทียมกันของสตริงเช่น

myStr !== 'foo'

คุณสามารถลบล้างนอกการทดสอบได้หากคุณต้องการฟีเจอร์ regex (ที่นี่การทดสอบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่):

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

โซลูชัน regex ที่ด้านบนของคำตอบนี้อาจมีประโยชน์อย่างไรก็ตามในสถานการณ์ที่จำเป็นต้องใช้การทดสอบ regex เชิงบวก (อาจใช้ API)


สิ่งที่เกี่ยวกับช่องว่างต่อท้าย? เช่นถ้าฉันต้องการทดสอบล้มเหลวด้วยสตริง" hede "?
eagor

@eagor \sคำสั่งจับคู่อักขระช่องว่างเดียว
Roy Tinker

ขอบคุณ แต่ฉันไม่ได้จัดการปรับปรุง regex เพื่อให้งานนี้
eagor

2
@eagor:^(?!\s*hede\s*$).*
Roy Tinker

52

FWIW เนื่องจากภาษาปกติ (ภาษาอาคาที่มีเหตุผล) ปิดภายใต้การใช้งานร่วมกันจึงเป็นไปได้ที่จะหานิพจน์ทั่วไป แต่มีเครื่องมือไม่มากที่ใช้สิ่งนี้

Vcsnรองรับผู้ประกอบการนี้ (ซึ่งหมายถึง{c}postfix)

แรกที่คุณกำหนดประเภทของการแสดงออกของคุณ: ป้ายชื่อเป็นตัวอักษร ( lal_char) เพื่อเลือกจากaไปzเช่น (การกำหนดตัวอักษรเมื่อทำงานกับ complementation เป็นของหลักสูตรที่สำคัญมาก) และ "มูลค่า" คำนวณสำหรับแต่ละคำเป็นเพียงบูลีน : trueคำนี้ได้รับการยอมรับfalse, ปฏิเสธ

ใน 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}  𝔹

จากนั้นคุณป้อนการแสดงออกของคุณ:

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

แปลงนิพจน์นี้เป็นออโตเมติก:

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

หุ่นยนต์ที่สอดคล้องกัน

ในที่สุดแปลงหุ่นยนต์นี้กลับไปเป็นนิพจน์อย่างง่าย

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

ที่+มักจะมีการแสดง|, \eหมายถึงคำที่ว่างเปล่าและ[^]มักจะเขียน.(ตัวอักษรใด ๆ ) ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*ดังนั้นด้วยบิตของการเขียนใหม่

คุณสามารถดูตัวอย่างนี้ที่นี่และพยายาม Vcsn ออนไลน์มี


6
จริง แต่น่าเกลียดและใช้ได้สำหรับชุดอักขระขนาดเล็กเท่านั้น คุณไม่ต้องการทำสิ่งนี้กับสายอักขระ Unicode :-)
reinierpost

มีเครื่องมืออื่น ๆ อีกมากมายที่ช่วยให้มันเป็นหนึ่งในความเป็นอยู่ที่น่าประทับใจมากที่สุดคือRagel ที่นั่นจะมีการเขียนเป็น (ใด ๆ * - ('hehe' any *)) สำหรับการจับคู่เริ่มต้นหรือ (ใด ๆ * - ('hehe' any *)) สำหรับไม่ตรงแนว
Peter K

1
@reierierpost: ทำไมมันน่าเกลียดและมีปัญหากับยูนิโค้ด? ฉันไม่เห็นด้วยกับทั้งคู่ (ฉันไม่มีประสบการณ์กับ vcsn แต่มีกับ DFA)
Peter K

3
@PedroGimeno เมื่อคุณทอดสมอคุณแน่ใจที่จะใส่ regex นี้ใน parens ก่อนหรือไม่ มิฉะนั้นความสำคัญระหว่างจุดยึดและ|จะไม่เล่นอย่างสวยงาม '^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'.
akim

1
ฉันคิดว่ามันคุ้มค่าที่จะกล่าวว่าวิธีนี้เหมาะสำหรับการจับคู่บรรทัดที่ไม่ใช่คำว่า 'hede' แทนที่จะเป็นบรรทัดมากกว่าไม่มีคำว่า 'hede' ซึ่งเป็นสิ่งที่ OP ต้องการ ดูคำตอบของฉันสำหรับหลัง
Pedro Gimeno

51

ต่อไปนี้เป็นคำอธิบายที่ดีว่าทำไมจึงไม่ง่ายที่จะปฏิเสธ regex โดยพลการ ฉันต้องเห็นด้วยกับคำตอบอื่น ๆ แม้ว่า: ถ้านี่คือสิ่งอื่นที่ไม่ใช่คำถามสมมุติแล้ว regex ไม่ใช่ตัวเลือกที่เหมาะสมที่นี่


10
เครื่องมือบางชนิดและโดยเฉพาะ mysqldumpslow มีเพียงวิธีนี้ในการกรองข้อมูลดังนั้นในกรณีเช่นนี้การหา regex เพื่อทำสิ่งนี้เป็นทางออกที่ดีที่สุดนอกเหนือจากการเขียนเครื่องมือใหม่ (โปรแกรมแก้ไขต่างๆสำหรับ MySQL AB / Sun / Oracle.
FGM

1
ตรงกับสถานการณ์ของฉัน เครื่องมือเท็มเพลต Velocity ใช้นิพจน์ทั่วไปเพื่อตัดสินใจว่าจะใช้การแปลงเมื่อใด (escape html) และฉันต้องการให้มันทำงานยกเว้นทุกครั้งในสถานการณ์เดียว
Henno Vermeulen

1
มีทางเลือกอะไรอีกบ้าง? ฉันไม่เคยพบอะไรที่สามารถทำการจับคู่สตริงที่แม่นยำนอกเหนือจาก regex ถ้า OP กำลังใช้ภาษาการเขียนโปรแกรมอาจมีเครื่องมืออื่นให้ใช้ แต่ถ้าเขา / เธอกำลังใช้งานอยู่ไม่ได้เขียนโค้ดอยู่อาจไม่มีทางเลือกอื่น
kingfrito_5005

2
หนึ่งในสถานการณ์ที่ไม่ใช่สมมุติฐานที่ regex เป็นตัวเลือกที่ดีที่สุด: ฉันอยู่ใน IDE (Android Studio) ที่แสดงเอาต์พุตของบันทึกข้อมูลและเครื่องมือการกรองเพียงอย่างเดียวคือสตริงธรรมดาและ regex การพยายามทำสิ่งนี้ด้วยสตริงธรรมดาจะล้มเหลวโดยสิ้นเชิง
LarsH

48

ด้วย lookahead เชิงลบนิพจน์ทั่วไปสามารถจับคู่สิ่งที่ไม่มีรูปแบบเฉพาะ นี่คือคำตอบและอธิบายโดย Bart Kiers คำอธิบายที่ดี!

อย่างไรก็ตามด้วยคำตอบของ Bart Kiers ส่วน lookahead จะทดสอบ 1 ถึง 4 ตัวอักษรล่วงหน้าขณะเดียวกัน เราสามารถหลีกเลี่ยงปัญหานี้และปล่อยให้ส่วน lookahead ตรวจสอบข้อความทั้งหมดตรวจสอบให้แน่ใจว่าไม่มี 'hede' จากนั้นส่วนปกติ (. *) สามารถกินข้อความทั้งหมดได้ในคราวเดียว

นี่คือ regex ที่ปรับปรุงใหม่:

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

โปรดทราบว่า (*?) lazy quantifier ในส่วน lookahead เชิงลบเป็นตัวเลือกคุณสามารถใช้ (*) โลภ quantifier แทนขึ้นอยู่กับข้อมูลของคุณ: หาก 'hede' ปรากฏขึ้นและในช่วงครึ่งแรกของข้อความ จะเร็วขึ้น มิฉะนั้นปริมาณโลภจะเร็วขึ้น อย่างไรก็ตามหาก 'hede' ไม่ปรากฏขึ้นทั้งคู่ก็จะช้าเหมือนกัน

นี่คือรหัสตัวอย่างรหัสการสาธิต

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ lookahead โปรดตรวจสอบบทความดีดี: Mastering Lookahead และ Lookbehind

นอกจากนี้โปรดตรวจสอบRegexGen.jsซึ่งเป็นตัวสร้างนิพจน์ทั่วไปของ JavaScript ที่ช่วยในการสร้างนิพจน์ปกติที่ซับซ้อน ด้วย RegexGen.js คุณสามารถสร้าง regex ด้วยวิธีที่อ่านได้มากขึ้น:

var _ = regexGen;

var regex = _(
    _.startOfLine(),             
    _.anything().notContains(       // match anything that not contains:
        _.anything().lazy(), 'hede' //   zero or more chars that followed by 'hede',
                                    //   i.e., anything contains 'hede'
    ), 
    _.endOfLine()
);

3
ดังนั้นเพียงแค่ตรวจสอบว่าสตริงที่กำหนดไม่มี str1 และ str2:^(?!.*(str1|str2)).*$
S.Serpooshan

1
ใช่หรือคุณสามารถใช้ตัววัดปริมาณขี้เกียจ: ^(?!.*?(?:str1|str2)).*$ขึ้นกับข้อมูลของคุณ เพิ่ม?:เนื่องจากเราไม่จำเป็นต้องจับภาพ
amobiz

นี่คือคำตอบที่ดีที่สุดโดยปัจจัย 10 เท่า หากคุณเพิ่มรหัส jsfiddle ของคุณและผลลัพธ์ลงในคำตอบที่คนอาจสังเกตเห็นมัน ฉันสงสัยว่าทำไมรุ่นขี้เกียจจึงเร็วกว่ารุ่นโลภเมื่อไม่มีเฮด พวกเขาไม่ควรใช้เวลาเท่ากันหรือ
user5389726598465

ใช่พวกเขาใช้เวลาเท่ากันเนื่องจากทั้งคู่ทดสอบข้อความทั้งหมด
amobiz

41

มาตรฐาน

ฉันตัดสินใจที่จะประเมินตัวเลือกที่นำเสนอบางส่วนและเปรียบเทียบประสิทธิภาพของพวกเขารวมถึงใช้คุณสมบัติใหม่บางอย่าง การเปรียบเทียบใน. NET Regex Engine: http://regexhero.net/tester/

ข้อความมาตรฐาน:

7 บรรทัดแรกไม่ควรตรงกันเนื่องจากมีนิพจน์ที่ค้นหาขณะที่ 7 บรรทัดด้านล่างควรตรงกัน!

Regex Hero is a real-time online Silverlight Regular Expression Tester.
XRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero
egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester.
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester.

Regex Her
egex Hero
egex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester.
Nobody is a real-time online Silverlight Regular Expression Tester.
Regex Her o egex Hero Regex  Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester.

ผล:

ผลลัพธ์คือการวนซ้ำต่อวินาทีเนื่องจากค่ามัธยฐานของการวิ่ง 3 ครั้ง - จำนวนที่มากขึ้น = ดีกว่า

01: ^((?!Regex Hero).)*$                    3.914   // Accepted Answer
02: ^(?:(?!Regex Hero).)*$                  5.034   // With Non-Capturing group
03: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Lookahead only on the right first letter
04: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Match the word and check if you're still at linestart
05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logic Branch: Find Regex Hero? match nothing, else anything

P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logic Branch in Perl - Quick FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direct COMMIT & FAIL in Perl

ตั้งแต่. NET ไม่สนับสนุนการกระทำกริยา (* FAIL เป็นต้น) ฉันไม่สามารถทดสอบวิธีแก้ปัญหา P1 และ P2 ได้

สรุป:

ฉันพยายามทดสอบวิธีแก้ปัญหาที่เสนอมากที่สุดการเพิ่มประสิทธิภาพบางคำอาจทำได้สำหรับบางคำ ตัวอย่างเช่นหากตัวอักษรสองตัวแรกของสตริงการค้นหาไม่เหมือนกันคำตอบ 03 สามารถขยายได้เพื่อ ^(?>[^R]+|R+(?!egex Hero))*$ให้ได้รับประสิทธิภาพเล็กน้อย

แต่วิธีแก้ปัญหาที่เร็วที่สุดที่อ่านง่ายและมีประสิทธิภาพที่สุดโดยรวมนั้นดูเหมือนว่าจะเป็น 05 โดยใช้ประโยคคำสั่งแบบมีเงื่อนไขหรือ 04 ด้วยตัวระบุปริมาณ ฉันคิดว่าโซลูชั่น Perl ควรเร็วขึ้นและอ่านง่ายขึ้น


5
คุณควรมีเวลา^(?!.*hede)ด้วย /// นอกจากนี้อาจเป็นการดีกว่าที่จะจัดอันดับนิพจน์สำหรับคลังข้อมูลที่ตรงกันและคลังข้อมูลที่ไม่ตรงกันเนื่องจากมักเป็นกรณีที่การจับคู่ส่วนใหญ่หรือส่วนใหญ่ไม่ตรง
ikegami

32

ไม่ใช่ regex แต่ฉันพบว่ามันมีเหตุผลและมีประโยชน์ในการใช้ greps อนุกรมที่มีไพพ์เพื่อกำจัดสัญญาณรบกวน

เช่น. ค้นหาไฟล์ config ของ Apache โดยไม่ต้องคอมเม้นท์ทั้งหมด

grep -v '\#' /opt/lampp/etc/httpd.conf      # this gives all the non-comment lines

และ

grep -v '\#' /opt/lampp/etc/httpd.conf |  grep -i dir

ตรรกะของซีเรียลอนุกรมคือ (ไม่ใช่ความคิดเห็น) และ (ตรงกับ dir)


2
ฉันคิดว่าเขากำลังขอรุ่น regex ของgrep -v
Angel.King.47

9
สิ่งนี้เป็นอันตราย ยังขาดสายเช่นgood_stuff #comment_stuff
Xavi Montero

29

ด้วยวิธีนี้คุณหลีกเลี่ยงการทดสอบ lookahead ในแต่ละตำแหน่ง:

/^(?:[^h]+|h++(?!ede))*+$/

เทียบเท่ากับ (สำหรับ. net):

^(?>(?:[^h]+|h+(?!ede))*)$

คำตอบเก่า:

/^(?>[^h]+|h+(?!ede))*$/

7
จุดดี; ฉันประหลาดใจที่ไม่มีใครพูดถึงวิธีการนี้มาก่อน อย่างไรก็ตาม regex นั้นมีแนวโน้มที่จะbacktracking รุนแรงเมื่อนำไปใช้กับข้อความที่ไม่ตรงกัน นี่คือวิธีที่ฉันจะทำ:/^[^h]*(?:h+(?!ede)[^h]*)*$/
Alan Moore

... หรือคุณเพียงแค่ทำให้ปริมาณทั้งหมดเป็นกรรมสิทธิ์ ;)
Alan Moore

@ Alan Moore - ฉันก็ประหลาดใจเช่นกัน ฉันเห็นความคิดเห็นของคุณ (และ regex ที่ดีที่สุดในกอง) ที่นี่เฉพาะหลังจากโพสต์รูปแบบเดียวกันนี้ในคำตอบด้านล่าง
ridgerunner

@ridgerunner ไม่จำเป็นต้องเป็นสิ่งที่ดีที่สุด ฉันเห็นการวัดประสิทธิภาพที่คำตอบยอดนิยมดีกว่า (ฉันประหลาดใจเกี่ยวกับสรรพสินค้าใหญ่)
Qtax

23

ดังกล่าว(?:(?!hede).)*เป็นสิ่งที่ดีเพราะสามารถยึดได้

^(?:(?!hede).)*$               # A line without hede

foo(?:(?!hede).)*bar           # foo followed by bar, without hede between them

แต่สิ่งต่อไปนี้จะเพียงพอในกรณีนี้:

^(?!.*hede)                    # A line without hede

การทำให้เข้าใจง่ายนี้พร้อมที่จะเพิ่มส่วนคำสั่ง "AND":

^(?!.*hede)(?=.*foo)(?=.*bar)   # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar       # Same

20

นี่คือวิธีที่ฉันจะทำ:

^[^h]*(h(?!ede)[^h]*)*$

แม่นยำและมีประสิทธิภาพมากกว่าคำตอบอื่น ๆ มันใช้เทคนิคประสิทธิภาพของ"unrolling-the-loop"ของ Friedl และต้องใช้การย้อนรอยน้อยกว่ามาก


17

หากคุณต้องการจับคู่อักขระเพื่อคัดค้านคำที่คล้ายกับคัดค้านคลาสอักขระ:

ตัวอย่างเช่นสตริง:

<?
$str="aaa        bbb4      aaa     bbb7";
?>

ไม่ได้ใช้:

<?
preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches);
?>

ใช้:

<?
preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches);
?>

การแจ้งให้ทราบ"(?!bbb)."ล่วงหน้าไม่ใช่การมองทั้งหลังหรือการมองหน้ามันเป็น lookcurrent ตัวอย่างเช่น:

"(?=abc)abcde", "(?!abc)abcde"

3
ไม่มี "lookcurrent" ใน perl regexp's นี่คือ lookahead เชิงลบอย่างแท้จริง (คำนำหน้า(?!) คำนำหน้าของ lookahead เชิงบวกจะเป็น(?=ในขณะที่คำนำหน้า lookbehind ที่สอดคล้องกันจะเป็น(?<!และ(?<=ตามลำดับ Lookahead หมายถึงคุณอ่านตัวอักษรถัดไป Lookbehind หมายถึงคุณตรวจสอบอักขระที่ถูกใช้ไปแล้ว
Didier L

14

คำตอบยอดนิยมในตัวแปรของฉันที่อ่านได้ง่ายกว่าคือ:

^(?!.*hede)

โดยพื้นฐานแล้ว "จับคู่ที่จุดเริ่มต้นของบรรทัดหากว่าไม่มี" hede "อยู่ในนั้น" - ดังนั้นความต้องการจึงถูกแปลเกือบเป็น regex โดยตรง

แน่นอนว่าเป็นไปได้ที่จะมีข้อกำหนดความล้มเหลวหลายประการ:

^(?!.*(hede|hodo|hada))

รายละเอียด: ^ anchor ทำให้มั่นใจได้ว่าเอ็นจิ้น regex จะไม่ลองการจับคู่ในทุกตำแหน่งในสตริงซึ่งจะจับคู่กับสตริงทั้งหมด

^ anchor ในจุดเริ่มต้นนั้นหมายถึงการแสดงจุดเริ่มต้นของบรรทัด เครื่องมือ grep จับคู่แต่ละบรรทัดทีละครั้งในบริบทที่คุณทำงานกับสตริงหลายบรรทัดคุณสามารถใช้แฟล็ก "m":

/^(?!.*hede)/m # JavaScript syntax

หรือ

(?m)^(?!.*hede) # Inline flag

ตัวอย่างที่ยอดเยี่ยมกับการปฏิเสธหลาย
Peter Parada

ข้อแตกต่างอย่างหนึ่งจากคำตอบยอดนิยมคือสิ่งนี้ไม่ตรงกับสิ่งใดและตรงกับทั้งบรรทัดหากไม่มี "hede"
Z. Khullah

13

OP ไม่ได้ระบุหรือ Tagโพสต์เพื่อระบุบริบท (ภาษาการเขียนโปรแกรมเครื่องมือแก้ไข) Regex จะถูกใช้ภายใน

Textpadสำหรับผมบางครั้งผมต้องทำเช่นนี้ในขณะที่แก้ไขไฟล์โดยใช้

Textpad รองรับ Regex บางตัว แต่ไม่รองรับ lookahead หรือ lookbehind ดังนั้นจึงใช้เวลาไม่กี่ขั้นตอน

หากฉันต้องการเก็บทุกบรรทัดที่ไม่มีสตริงhedeฉันจะทำดังนี้:

1. ค้นหา / แทนที่ไฟล์ทั้งหมดเพื่อเพิ่ม "แท็ก" ที่ไม่ซ้ำกันในตอนต้นของแต่ละบรรทัดที่มีข้อความใด ๆ

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. ลบบรรทัดทั้งหมดที่มีสตริงhede(สตริงการแทนที่ว่างเปล่า):

    Search string:<@#-unique-#@>.*hede.*\n  
    Replace string:<nothing>  
    Replace-all  

3. ณ จุดนี้เส้นที่เหลือทั้งหมดอย่าhedeมีสตริง ลบ "แท็ก" ที่ไม่ซ้ำออกจากทุกบรรทัด (สตริงการแทนที่ว่างเปล่า):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

ตอนนี้คุณมีข้อความต้นฉบับที่มีบรรทัดทั้งหมดที่มีการhedeลบ สตริง


หากฉันต้องการทำบางสิ่งบางอย่างไปยังบรรทัดที่ไม่มีสตริงhedeฉันจะทำดังนี้:

1. ค้นหา / แทนที่ไฟล์ทั้งหมดเพื่อเพิ่ม "แท็ก" ที่ไม่ซ้ำกันในตอนต้นของแต่ละบรรทัดที่มีข้อความใด ๆ

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. สำหรับทุกบรรทัดที่มีสตริงhedeเอา "แท็ก" ที่ไม่ซ้ำออก:

    Search string:<@#-unique-#@>(.*hede)
    Replace string:\1  
    Replace-all  

3. ณ จุดนี้ทุกบรรทัดที่ขึ้นต้นด้วยไม่ซ้ำกัน "แท็ก", อย่าhedeมีสตริง ตอนนี้ฉันสามารถทำบางสิ่งบางอย่างของฉันไปที่บรรทัดเหล่านั้นเท่านั้น

4. เมื่อเสร็จแล้วฉันจะลบ "แท็ก" ที่ไม่ซ้ำออกจากทุกบรรทัด (สตริงการแทนที่ว่างเปล่า):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

12

เนื่องจากไม่มีใครให้คำตอบกับคำถามโดยตรง ที่ถามฉันจะทำ

คำตอบคือด้วย POSIX grepมันเป็นไปไม่ได้ที่จะตอบสนองคำขอนี้:

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

เหตุผลก็คือ POSIX grepจำเป็นต้องใช้กับนิพจน์ปกติพื้นฐานเท่านั้นเท่านั้นซึ่งไม่ทรงพลังเพียงพอสำหรับการทำภารกิจนั้นให้สำเร็จ (ไม่สามารถแยกวิเคราะห์ภาษาปกติได้เนื่องจากไม่มีการสลับและวงเล็บ)

อย่างไรก็ตาม GNU grepใช้ส่วนขยายที่อนุญาต โดยเฉพาะอย่างยิ่ง\|เป็นตัวดำเนินการสำรองในการนำ BREs ไปใช้ของ GNU \(และ\)เป็นเครื่องหมายวงเล็บ หากเอ็นจิ้นนิพจน์ทั่วไปของคุณรองรับการสับเปลี่ยนวงเล็บเหลี่ยมนิพจน์วงเล็บและดาว Kleene และสามารถยึดกับจุดเริ่มต้นและจุดสิ้นสุดของสตริงนั่นคือทั้งหมดที่คุณต้องการสำหรับวิธีนี้ อย่างไรก็ตามโปรดทราบว่าชุดเชิงลบ[^ ... ]มีความสะดวกมากนอกเหนือจากชุดนั้นเพราะมิฉะนั้นคุณจะต้องแทนที่ด้วยการแสดงออกของแบบฟอร์ม(a|b|c| ... )ที่แสดงรายการตัวละครทุกตัวที่ไม่ได้อยู่ในชุดซึ่งน่าเบื่อมากและยาวเกินไปดังนั้นยิ่งถ้า ชุดอักขระทั้งหมดคือ Unicode

ด้วย GNU grepคำตอบจะเป็นดังนี้:

grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" input

(พบกับGrailและการเพิ่มประสิทธิภาพบางอย่างด้วยมือ)

คุณยังสามารถใช้เครื่องมือที่ใช้Extended Regular Expressionเช่นegrepเพื่อกำจัดแบ็กสแลช:

egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" input

นี่คือสคริปต์สำหรับทดสอบ (โปรดสังเกตว่ามันจะสร้างไฟล์testinput.txtในไดเรกทอรีปัจจุบัน):

#!/bin/bash
REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$"

# First four lines as in OP's testcase.
cat > testinput.txt <<EOF
hoho
hihi
haha
hede

h
he
ah
head
ahead
ahed
aheda
ahede
hhede
hehede
hedhede
hehehehehehedehehe
hedecidedthat
EOF
diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt)

ในระบบของฉันมันพิมพ์:

Files /dev/fd/63 and /dev/fd/62 are identical

อย่างที่คาดไว้.

สำหรับผู้ที่สนใจในรายละเอียดเทคนิคที่ใช้คือการแปลงการแสดงออกปกติที่ตรงกับคำว่าเป็นหุ่นยนต์ที่มีขอบเขต จำกัด จากนั้นเปลี่ยนออโตมาตาด้วยการเปลี่ยนทุกสถานะการยอมรับให้เป็นแบบไม่ยอมรับและในทางกลับกัน การแสดงออกปกติ

ในที่สุดตามที่ทุกคนได้กล่าวไว้หากเอ็นจิ้นนิพจน์ปกติของคุณรองรับ lookahead เชิงลบนั่นจะทำให้งานง่ายขึ้นมาก ตัวอย่างเช่นด้วย GNU grep:

grep -P '^((?!hede).)*$' input

อัปเดต:ฉันเพิ่งค้นพบไลบรารี่ OfficialTheory ที่ยอดเยี่ยมของ Kendall Hopkins ซึ่งเขียนด้วย PHP ซึ่งมีฟังก์ชั่นคล้ายกับ Grail การใช้และตัวขยายเสียงที่เขียนด้วยตัวเองฉันสามารถเขียนตัวสร้างออนไลน์ของนิพจน์ปกติเชิงลบที่ให้วลีป้อนข้อมูล (รองรับเฉพาะตัวอักษรและตัวเลขและอักขระเว้นวรรคในขณะนี้): http://www.formauri.es/personal/ pgimeno / อื่น ๆ / ไม่แสวงหาผลการแข่งขัน regex /

สำหรับhedeมันจะออกผลลัพธ์:

^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$

ซึ่งเทียบเท่ากับข้างต้น


11

นับตั้งแต่เปิดตัว ruby-2.4.1 เราสามารถใช้งานใหม่ได้ Absent Operatorในการแสดงผลปกติของรูบี้

จากเอกสารอย่างเป็นทางการ

(?~abc) matches: "", "ab", "aab", "cccc", etc.
It doesn't match: "abc", "aabc", "ccccabc", etc.

ดังนั้นในกรณีของคุณ^(?~hede)$ทำงานให้คุณ

2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)}
 => ["hoho", "hihi", "haha"]

9

ผ่านคำกริยา PCRE (*SKIP)(*F)

^hede$(*SKIP)(*F)|^.*$

นี่จะข้ามบรรทัดที่มีสตริงที่แน่นอน hedeตรงทั้งหมดและตรงกับบรรทัดที่เหลือทั้งหมด

การสาธิต

การดำเนินการของชิ้นส่วน:

ขอให้เราพิจารณา regex ข้างต้นโดยแยกออกเป็นสองส่วน

  1. ส่วนก่อน|สัญลักษณ์ ส่วนที่ไม่ควรถูกจับคู่

    ^hede$(*SKIP)(*F)
  2. ส่วนหลัง|สัญลักษณ์ ส่วนที่ควรจะจับคู่

    ^.*$

ส่วนที่ 1

เอ็นจิ้น Regex จะเริ่มการทำงานจากส่วนแรก

^hede$(*SKIP)(*F)

คำอธิบาย:

  • ^ ยืนยันว่าเราเป็นจุดเริ่มต้น
  • hede จับคู่สตริง hede
  • $ ยืนยันว่าเราอยู่ท้ายบรรทัด

ดังนั้นบรรทัดที่มีสตริงhedeจะจับคู่ เมื่อเอ็นจิ้น regex เห็นสิ่งต่อไปนี้(*SKIP)(*F)( หมายเหตุ: คุณสามารถเขียน(*F)เป็น(*FAIL) ) กริยามันจะข้ามและทำให้การแข่งขันล้มเหลว |เรียกว่าการเปลี่ยนแปลงหรือตรรกะหรือผู้ประกอบการเพิ่มถัด PCRE กริยาซึ่ง inturn hedeตรงกับเขตแดนทั้งหมดที่มีอยู่ระหว่างแต่ละคนและตัวละครทุกตัวในทุกสายยกเว้นสายมีสตริงที่แน่นอน ดูการสาธิตที่นี่ นั่นคือพยายามจับคู่อักขระจากสตริงที่เหลือ ตอนนี้จะดำเนินการ regex ในส่วนที่สอง

ส่วนที่ 2

^.*$

คำอธิบาย:

  • ^ ยืนยันว่าเราเป็นจุดเริ่มต้น นั่นคือมันตรงกับทุกบรรทัดเริ่มยกเว้นหนึ่งในhedeบรรทัด ดูการสาธิตที่นี่
  • .*ในโหมด Multiline .จะจับคู่กับอักขระใด ๆ ยกเว้นอักขระขึ้นบรรทัดใหม่หรืออักขระขึ้นบรรทัดใหม่ และ*จะทำซ้ำตัวอักษรก่อนหน้าเป็นศูนย์หรือมากกว่าครั้ง ดังนั้น.*จะตรงกับสายทั้งหมด ดูตัวอย่างได้ที่นี่ที่นี่

    เฮ้ทำไมคุณเพิ่ม * แทน. +?

    เพราะ.*จะจับคู่บรรทัดว่าง แต่.+จะไม่จับคู่ว่าง เราต้องการจับคู่ทุกบรรทัดยกเว้นhedeอาจมีความเป็นไปได้ของบรรทัดว่างในอินพุตเช่นกัน ดังนั้นคุณต้องใช้แทน.* จะซ้ำอักขระก่อนหน้าอย่างน้อยหนึ่งครั้ง ดูตรงบรรทัดที่ว่างเปล่าที่นี่.+.+.*

  • $ จุดสิ้นสุดของจุดยึดบรรทัดไม่จำเป็นที่นี่


7

มันอาจจะรักษาได้ดีกว่าสำหรับ regexes สองตัวในโค้ดของคุณหนึ่งอันเพื่อทำการจับคู่ครั้งแรกและถ้ามันจับคู่รัน regex ที่สองเพื่อตรวจสอบกรณีที่ผิดปกติที่คุณต้องการบล็อกตัวอย่างเช่น^.*(hede).*มีตรรกะที่เหมาะสมในรหัสของคุณ

ตกลงฉันยอมรับว่านี่ไม่ใช่คำตอบสำหรับคำถามที่โพสต์และจริงๆแล้วมันอาจใช้การประมวลผลมากกว่า regex เพียงเล็กน้อย แต่สำหรับนักพัฒนาที่มาที่นี่เพื่อค้นหาการแก้ไขฉุกเฉินอย่างรวดเร็วสำหรับกรณีที่มีค่าผิดปกติวิธีนี้ไม่ควรมองข้ามโซลูชันนี้


5

TXR ภาษาสนับสนุนการปฏิเสธ regex

$ txr -c '@(repeat)
@{nothede /~hede/}
@(do (put-line nothede))
@(end)'  Input

ตัวอย่างที่ซับซ้อนมากขึ้น: จับคู่ทุกบรรทัดที่ขึ้นต้นด้วยaและลงท้ายด้วยzแต่ไม่มีสตริงย่อยhede:

$ txr -c '@(repeat)
@{nothede /a.*z&~.*hede.*/}
@(do (put-line nothede))
@(end)' -
az         <- echoed
az
abcz       <- echoed
abcz
abhederz   <- not echoed; contains hede
ahedez     <- not echoed; contains hede
ace        <- not echoed; does not end in z
ahedz      <- echoed
ahedz

Regex negation นั้นไม่ได้มีประโยชน์อะไรเป็นพิเศษ แต่เมื่อคุณมีการตัดกันสิ่งต่าง ๆ น่าสนใจเนื่องจากคุณมีชุดบูลีนแบบเต็มชุด: คุณสามารถแสดง "เซตที่ตรงกับสิ่งนี้ยกเว้นสิ่งที่ตรงกับที่"


โปรดทราบว่ายังเป็นโซลูชันสำหรับ Regex ของ ElasticSearch Lucene
Wiktor Stribiżew

5

อีกทางเลือกหนึ่งคือการเพิ่มการมองไปข้างหน้าในเชิงบวกและตรวจสอบว่าheheมีที่ใดก็ได้ในบรรทัดอินพุตจากนั้นเราจะคัดค้านด้วยนิพจน์ที่คล้ายกับ:

^(?!(?=.*\bhede\b)).*$

มีขอบเขตของคำ


นิพจน์นั้นอธิบายไว้ที่แผงด้านบนขวาของregex101.comหากคุณต้องการสำรวจ / ทำให้ง่ายขึ้น / แก้ไขและในลิงค์นี้คุณสามารถดูว่าจะจับคู่กับอินพุตตัวอย่างบางตัวได้อย่างไรหากคุณต้องการ


วงจร RegEx

jex.imเห็นภาพการแสดงออกปกติ:

ป้อนคำอธิบายรูปภาพที่นี่


4

ฟังก์ชั่นด้านล่างจะช่วยให้คุณได้รับผลลัพธ์ที่ต้องการ

<?PHP
      function removePrepositions($text){

            $propositions=array('/\bfor\b/i','/\bthe\b/i'); 

            if( count($propositions) > 0 ) {
                foreach($propositions as $exceptionPhrase) {
                    $text = preg_replace($exceptionPhrase, '', trim($text));

                }
            $retval = trim($text);

            }
        return $retval;
    }


?>

2

^ ((?! hede).) * $ เป็นคำตอบที่สวยงามยกเว้นเนื่องจากมันใช้อักขระคุณจะไม่สามารถรวมเข้ากับเกณฑ์อื่นได้ ตัวอย่างเช่นสมมติว่าคุณต้องการตรวจสอบการไม่มี "hede" และ "haha" วิธีนี้ใช้ได้ผลเพราะจะไม่ใช้ตัวอักษร:

^ (?!. \ bhede \ b) (? =. \ bhaha \ b)


1

วิธีใช้คำกริยาควบคุมการย้อนรอยของ PCRE เพื่อจับคู่บรรทัดที่ไม่มีคำ

นี่เป็นวิธีที่ฉันไม่เคยเห็นมาก่อน:

/.*hede(*COMMIT)^|/

มันทำงานอย่างไร

ก่อนอื่นจะพยายามหา "hede" ที่ใดที่หนึ่งในบรรทัด หากประสบความสำเร็จ ณ จุดนี้(*COMMIT)บอกเครื่องยนต์ถึงไม่เพียง แต่ไม่ย้อนรอยในกรณีที่เกิดความล้มเหลว แต่ยังไม่พยายามทำการจับคู่เพิ่มเติมในกรณีนั้น จากนั้นเราพยายามจับคู่สิ่งที่ไม่สามารถจับคู่ได้ (ในกรณีนี้^)

หากบรรทัดไม่มี "hede" ดังนั้นทางเลือกที่สอง subpattern ว่างตรงกับสตริงหัวเรื่อง

วิธีนี้ไม่มีประสิทธิภาพมากกว่า lookahead เชิงลบ แต่ฉันคิดว่าฉันจะทิ้งมันไว้ที่นี่ในกรณีที่มีคนพบว่ามันเก๋และหามันใช้สำหรับแอปพลิเคชันอื่นที่น่าสนใจกว่า


0

ทางออกที่ง่ายกว่าคือใช้ตัวดำเนินการที่ไม่ใช่!

คำสั่งifของคุณจะต้องตรงกับ "มี" และไม่ตรงกับ "แยก"

var contains = /abc/;
var excludes =/hede/;

if(string.match(contains) && !(string.match(excludes))){  //proceed...

ฉันเชื่อว่านักออกแบบของ RegEx คาดว่าจะใช้งานตัวดำเนินการไม่ได้


0

บางทีคุณอาจพบสิ่งนี้บน Google ในขณะที่พยายามเขียน regex ที่สามารถจับคู่เซ็กเมนต์ของบรรทัด (ตรงข้ามกับทั้งบรรทัด) ซึ่งไม่ได้ซับสตริง เอาฉันสักครู่เพื่อคิดออกดังนั้นฉันจะแบ่งปัน:

รับสาย: <span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>

ฉันต้องการจับคู่ <span>แท็กที่ไม่มีสตริงย่อย "ไม่ดี"

/<span(?:(?!bad).)*?>จะจับคู่<span class=\"good\">และ<span class=\"ugly\">และ

โปรดสังเกตว่ามีวงเล็บสองชุด (เลเยอร์):

  • ด้านในสุดใช้สำหรับการมองเชิงลบ (ไม่ใช่กลุ่มการจับ)
  • ด้านนอกสุดถูกตีความโดย Ruby ในฐานะกลุ่มการดักจับ แต่เราไม่ต้องการให้เป็นกลุ่มการดักจับดังนั้นฉันจึงเพิ่ม?: ตอนนี้มันเริ่มแล้วและมันจะไม่ถูกตีความว่าเป็นกลุ่มการดักจับอีกต่อไป

ตัวอย่างในทับทิม:

s = '<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>'
s.scan(/<span(?:(?!bad).)*?>/)
# => ["<span class=\"good\">", "<span class=\"ugly\">"]

0

ด้วยConyEditคุณสามารถใช้บรรทัดคำสั่งcc.gl !/hede/เพื่อรับบรรทัดที่ไม่มีการจับคู่ regex หรือใช้บรรทัดคำสั่งcc.dl /hede/เพื่อลบบรรทัดที่มีการจับคู่ regex พวกเขามีผลเหมือนกัน


0

ฉันต้องการเพิ่มอีกตัวอย่างหนึ่งหากคุณพยายามจับคู่ทั้งบรรทัดที่มีสตริงXแต่ไม่มีสตริงY Y

ตัวอย่างเช่นสมมติว่าเราต้องการตรวจสอบว่า URL / สตริงของเรามี " Delicious-treats " ตราบใดที่มันไม่มี " chocolate " อยู่ด้วย

รูปแบบ regex นี้จะใช้งานได้ (ทำงานใน JavaScript ด้วย)

^(?=.*?tasty-treats)((?!chocolate).)*$

(ตัวอย่างโกลบอลแฟล็กหลายบรรทัด)

ตัวอย่างแบบโต้ตอบ: https://regexr.com/53gv4

ไม้ขีด

(URL เหล่านี้มี "Delicious-treats" และยังไม่มี "chocolate")

  • example.com/tasty-treats/strawberry-ice-cream
  • example.com/desserts/tasty-treats/banana-pudding
  • example.com/tasty-treats-overview

ไม่ตรงกัน

(URL เหล่านี้มี "ช็อคโกแลต" อยู่ที่ไหนสักแห่ง - ดังนั้นพวกเขาจะไม่ตรงกันแม้ว่าจะมี "อร่อย - ถือว่า")

  • example.com/tasty-treats/chocolate-cake
  • example.com/home-cooking/oven-roasted-chicken
  • example.com/tasty-treats/banana-chocolate-fudge
  • example.com/desserts/chocolate/tasty-treats
  • example.com/chocolate/tasty-treats/desserts
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.