อะไรคือความแตกต่างระหว่าง ?:, ?! และ? = ใน regex?


118

ฉันค้นหาความหมายของนิพจน์เหล่านี้ แต่ไม่เข้าใจความแตกต่างที่แท้จริงระหว่างนิพจน์เหล่านี้ นี่คือสิ่งที่พวกเขาพูด:

  • ?: จับคู่นิพจน์ แต่อย่าจับมัน
  • ?= จับคู่คำต่อท้าย แต่ไม่รวมไว้ในการจับภาพ
  • ?! จับคู่หากไม่มีคำต่อท้าย

ฉันลองใช้สิ่งเหล่านี้ใน RegEx อย่างง่ายและได้ผลลัพธ์ที่คล้ายกันสำหรับทุกคน ตัวอย่าง: 3 นิพจน์ต่อไปนี้ให้ผลลัพธ์ที่คล้ายกันมาก

  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?!\.[a-zA-Z0-9]+)*
  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?=\.[a-zA-Z0-9]+)*
  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9]+)*

โปรดแสดงกรณีทดสอบของคุณ ไม่ควรให้ผลลัพธ์เดียวกัน
Bergi

@ sepp2k ผลลัพธ์ที่เหมือนกันในไม่กี่กรณีหนึ่งในนั้นกล่าวถึงในคำถาม
RK Poddar

@Bergi ฉันทดสอบด้วยข้อมูลแบบสุ่มซึ่งมีคำภาษาอังกฤษหมายเลขโทรศัพท์ URL ที่อยู่อีเมลหมายเลข ฯลฯ
RK Poddar

4
@RKAgarwal อาฉันเห็นสิ่งที่คุณทำที่นั่น คุณเพิ่ม*หลังกลุ่มดังนั้นพวกเขาจึงถูกเพิกเฉย
sepp2k

หมายเหตุ noobie : คุณจะใช้สิ่งเหล่านี้ที่จุดเริ่มต้นของวงเล็บเท่านั้นและวงเล็บจะสร้างกลุ่มการจับภาพ (ชุดวงเล็บที่แตกต่างกันจะแยกส่วนต่างๆของข้อความ)
Ryan Taylor

คำตอบ:


171

ความแตกต่างระหว่าง?=และ?!คืออดีตต้องการให้นิพจน์ที่กำหนดเพื่อจับคู่และส่วนหลังต้องการให้ไม่ตรงกัน ตัวอย่างเช่นa(?=b)จะจับคู่ "a" ใน "ab" แต่ไม่ใช่ "a" ใน "ac" ในขณะที่a(?!b)จะจับคู่ "a" ใน "ac" แต่ไม่ใช่ "a" ใน "ab"

ความแตกต่างระหว่าง?:และ?=คือ?=ไม่รวมนิพจน์จากการจับคู่ทั้งหมดในขณะที่?:ไม่ได้สร้างกลุ่มการจับภาพ ตัวอย่างเช่นa(?:b)จะจับคู่ "ab" ใน "abc" ในขณะที่a(?=b)จะจับคู่กับ "a" ใน "abc" เท่านั้น a(b)จะจับคู่ "ab" ใน "abc" และสร้างการจับภาพที่มี "b"


87
?:  is for non capturing group
?=  is for positive look ahead
?!  is for negative look ahead
?<= is for positive look behind
?<! is for negative look behind

โปรดตรวจสอบที่นี่: http://www.regular-expressions.info/lookaround.htmlสำหรับบทแนะนำที่ดีมากและตัวอย่างเกี่ยวกับ lookahead ในนิพจน์ทั่วไป


15
แต่ JavaScript ไม่รู้จัก lookbehind
Bergi

1
อันนี้สมบูรณ์กว่าสำหรับ regex ทั่วไป
Yan Yang

/ (? <= ^ a) b / ทำงานให้ฉันในจาวาสคริปต์! ดูเหมือนว่าจะไม่มีบทช่วยสอนสำหรับการค้นหาเบื้องหลังใน Javascript บนอินเทอร์เน็ต
ย. โยชิอิ

มีเพียงเบราว์เซอร์เวอร์ชันล่าสุดเท่านั้นที่เริ่มรองรับการดูเบื้องหลังใน JS
anubhava

- anubhava ฉันไม่รู้ทางเลือกอื่นใดในการ / (? <= ^ a) b / โดยใช้นิพจน์ทั่วไปที่บริสุทธิ์ บางทีฉันอาจทำได้ แต่ฉันต้องพึ่งพาฟังก์ชันการโทรกลับ
ย. โยชิอิ

22

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

  • () กลุ่มการจับภาพ - นิพจน์ทั่วไปภายในวงเล็บต้องตรงกันและการจับคู่จะสร้างกลุ่มการจับภาพ
  • (?:) ไม่ใช่กลุ่มการจับภาพ - นิพจน์ทั่วไปภายในวงเล็บต้องตรงกัน แต่ไม่สร้างกลุ่มการจับภาพ
  • (?=) มองไปข้างหน้าในเชิงบวก - ยืนยันว่า regex จะต้องตรงกัน
  • (?!) มองไปข้างหน้าในแง่ลบ - ยืนยันว่าเป็นไปไม่ได้ที่จะจับคู่นิพจน์ทั่วไป

ลองนำไปใช้q(u)iในการเลิก qตรงกับคิวและกลุ่มจับuตรงU การจับคู่ภายในกลุ่มการจับภาพจะถูกจับและสร้างกลุ่มการจับภาพ iดังนั้นเครื่องยนต์ยังคงมี และiจะตรงกับi . ความพยายามในการแข่งขันครั้งสุดท้ายนี้ประสบความสำเร็จ quiถูกจับคู่และจับกลุ่มกับคุณจะถูกสร้างขึ้น

ลองนำไปใช้q(?:u)iในการเลิก อีกครั้งqตรงกับคิวและกลุ่มที่ไม่ได้จับuตรงU จับคู่จากกลุ่มที่ไม่ได้จับภาพ แต่ไม่ได้สร้างกลุ่มการจับภาพ iดังนั้นเครื่องยนต์ยังคงมี และiจะตรงกับi . ความพยายามในการแข่งขันครั้งสุดท้ายนี้ประสบความสำเร็จ quiถูกจับคู่

ลองนำไปใช้q(?=u)iในการเลิก Lookahead เป็นค่าบวกและตามด้วยโทเค็นอื่น อีกครั้งqตรงกับคิวและuตรงกับU อีกครั้งการแข่งขันจาก lookahead ที่ต้องทิ้งดังนั้นขั้นตอนเครื่องยนต์กลับมาจากiในสตริงเพื่อU การค้นหาประสบความสำเร็จเครื่องยนต์จึงทำงานต่อiไป แต่iไม่สามารถจับคู่ยู . ดังนั้นการจับคู่ครั้งนี้จึงล้มเหลว

ลองนำไปใช้q(?=u)uในการเลิก Lookahead เป็นค่าบวกและตามด้วยโทเค็นอื่น อีกครั้งqตรงกับคิวและuตรงกับU การแข่งขันจาก lookahead จะต้องทิ้งดังนั้นขั้นตอนเครื่องยนต์กลับมาจากuในสตริงเพื่อU การค้นหาประสบความสำเร็จเครื่องยนต์จึงทำงานต่อuไป และuจะตรงกับU ดังนั้นความพยายามในการแข่งขันครั้งนี้ประสบความสำเร็จ quถูกจับคู่

ลองนำไปใช้q(?!i)uในการเลิก แม้ในกรณีนี้ lookahead จะเป็นค่าบวก (เพราะiไม่ตรงกัน) และตามด้วยโทเค็นอื่น อีกครั้งqตรงกับคิวและiไม่ตรงกับU การแข่งขันจาก lookahead จะต้องทิ้งดังนั้นขั้นตอนเครื่องยนต์กลับมาจากuในสตริงเพื่อU การค้นหาประสบความสำเร็จเครื่องยนต์จึงทำงานต่อuไป และuจะตรงกับU ดังนั้นความพยายามในการแข่งขันครั้งนี้ประสบความสำเร็จ quถูกจับคู่

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


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

1
@ กรีนสิ่งสำคัญที่ต้องทำความเข้าใจเกี่ยวกับ lookahead และโครงสร้างการมองหาอื่น ๆ ก็คือแม้ว่าพวกเขาจะผ่านการเคลื่อนไหวเพื่อดูว่านิพจน์ย่อยของพวกเขาสามารถจับคู่ได้หรือไม่ แต่ก็ไม่ได้ "ใช้" ข้อความใด ๆ อาจจะสับสนเล็กน้อย
freedev

7

ลองจับคู่foobarกับสิ่งเหล่านี้:

/foo(?=b)(.*)/
/foo(?!b)(.*)/

regex แรกจะจับคู่และจะส่งคืน "bar" เป็นครั้งแรก - (?=b)ตรงกับ "b" แต่จะไม่ใช้มันทิ้งไว้ในวงเล็บต่อไปนี้

นิพจน์ที่สองจะไม่ตรงกันเนื่องจากคาดว่า "foo" จะตามด้วยสิ่งที่แตกต่างจาก "b"

(?:...)มีผลเหมือนกับ simple (...)แต่จะไม่ส่งคืนส่วนนั้นเป็น subatch


0

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


0

นี่คือความแตกต่างที่แท้จริง:

>>> re.match('a(?=b)bc', 'abc')
<Match...>
>>> re.match('a(?:b)c', 'abc')
<Match...>

# note:
>>> re.match('a(?=b)c', 'abc')
None

หากคุณไม่สนใจเนื้อหาหลัง "?:" หรือ "? =", "?:" และ "? =" จะเหมือนกัน ทั้งสองคนก็ใช้ได้

แต่ถ้าคุณต้องการเนื้อหาเหล่านั้นสำหรับขั้นตอนต่อไป (ไม่ใช่แค่จับคู่เนื้อหาทั้งหมดในกรณีนี้คุณสามารถใช้ "a (b)") คุณต้องใช้ "? =" แทน สาเหตุ "?:" จะผ่านพ้นมันไป

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.