Regex lookahead, lookbehind และกลุ่มอะตอม


314

ฉันพบสิ่งเหล่านี้ในร่างกายของฉัน regex แต่ฉันไม่ได้เบาะแสสิ่งที่ฉันสามารถใช้พวกเขา มีใครบางคนมีตัวอย่างเพื่อให้ฉันสามารถพยายามเข้าใจวิธีการทำงานของพวกเขา?

(?!) - negative lookahead
(?=) - positive lookahead
(?<=) - positive lookbehind
(?<!) - negative lookbehind

(?>) - atomic group

18
ทำไมเว็บไซต์ regex จึงไม่มีตารางง่ายๆเช่นนี้ แต่พวกเขามีกลุ่มข้อความอธิบายเท่านั้น regular-expressions.info/lookaround.html
whitecat

3
@ Whitecat ลอง: regex101.com regexr.com
Andrew

คำตอบ:


851

ตัวอย่าง

รับสายfoobarbarfoo:

bar(?=bar)     finds the 1st bar ("bar" which has "bar" after it)
bar(?!bar)     finds the 2nd bar ("bar" which does not have "bar" after it)
(?<=foo)bar    finds the 1st bar ("bar" which has "foo" before it)
(?<!foo)bar    finds the 2nd bar ("bar" which does not have "foo" before it)

คุณสามารถรวม:

(?<=foo)bar(?=bar)    finds the 1st bar ("bar" with "foo" before it and "bar" after it)

คำนิยาม

มองไปข้างหน้าในเชิงบวก (?=)

ค้นหา expression A โดยที่ expression B เป็นดังนี้:

A(?=B)

มองไปข้างหน้าเชิงลบ (?!)

ค้นหานิพจน์ A ที่นิพจน์ B ไม่ปฏิบัติตาม:

A(?!B)

มองด้านหลังเป็นบวก (?<=)

ค้นหา expression A โดยที่ expression B นำหน้า:

(?<=B)A

ดูด้านหลังติดลบ (?<!)

ค้นหานิพจน์ A โดยที่ expression B ไม่ได้นำหน้า:

(?<!B)A

กลุ่มอะตอม (?>)

กลุ่มอะตอมออกจากกลุ่มแล้วละทิ้งรูปแบบทางเลือกหลังจากรูปแบบที่จับคู่แรกภายในกลุ่ม (การย้อนรอยถูกปิดใช้งาน)

  • (?>foo|foot)sนำไปใช้กับfootsจะตรงกับทางเลือกที่ 1 fooจากนั้นล้มเหลวตามที่sไม่ได้ติดตามทันทีและหยุดเมื่อการย้อนรอยถูกปิดใช้งาน

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

  • (foo|foot)sนำไปใช้กับfootsจะ:

    1. จับคู่ทางเลือกที่ 1 fooจากนั้นล้มเหลวเนื่องจากsไม่ได้ติดตามทันทีfootsและย้อนกลับไปที่ตัวเลือกที่สอง
    2. จับคู่ทางเลือกที่ 2 footจากนั้นประสบความสำเร็จตามมาsทันทีfootsและหยุด

ทรัพยากรบางอย่าง

ผู้ทดสอบออนไลน์


1
ส่วน "การค้นหาแถบที่สอง" คุณหมายถึงอะไร มีเพียงแท่งเดียวในนิพจน์ / สตริง ขอบคุณ
ziggy

2
@ziggy สตริงที่กำลังทดสอบคือ "foobarbarfoo" อย่างที่คุณเห็นมีอยู่สองตัวและสองแถบในสตริง
skyfoot

4
บางคนสามารถอธิบายได้ว่าเมื่อใดที่ต้องมีกลุ่มอะตอม? หากฉันต้องการจับคู่กับตัวเลือกแรกทำไมฉันถึงต้องการให้หลายทางเลือก
arviman

2
คำอธิบายที่ดีขึ้นเกี่ยวกับกลุ่มอะตอมที่คำตอบนี้ บางคนสามารถแก้ไขที่นี่เพื่อทำคำตอบที่ไม่ใช่คำตอบนี้ได้ไหม
Peter Krauss

5
เพียงแค่ทราบว่าคำตอบนี้สำคัญเมื่อฉันลงเอยด้วยโครงการที่ต้องมีการสับแบบ regex นี่เป็นคำอธิบายที่ยอดเยี่ยมและกระชับเกี่ยวกับการมองไปรอบ ๆ
Tom Coughlin

215

Lookarounds เป็นการยืนยันความกว้างเป็นศูนย์ พวกเขาตรวจสอบ regex (ไปทางขวาหรือซ้ายของตำแหน่งปัจจุบัน - ขึ้นอยู่กับล่วงหน้าหรือหลัง) สำเร็จหรือล้มเหลวเมื่อพบการแข่งขัน (ขึ้นอยู่กับว่ามันเป็นบวกหรือลบ) และทิ้งส่วนที่จับคู่ พวกเขาไม่ใช้ตัวอักษรใด ๆ - การจับคู่สำหรับ regex ตามพวกเขา (ถ้ามี) จะเริ่มต้นที่ตำแหน่งเคอร์เซอร์เดียวกัน

อ่านประจำexpressions.infoสำหรับรายละเอียดเพิ่มเติม

  • Lookahead เชิงบวก:

ไวยากรณ์:

(?=REGEX_1)REGEX_2

จับคู่เฉพาะในกรณีที่ REGEX_1 ตรงกัน หลังจากการจับคู่ REGEX_1 การจับคู่จะถูกยกเลิกและการค้นหา REGEX_2 จะเริ่มต้นที่ตำแหน่งเดียวกัน

ตัวอย่าง:

(?=[a-z0-9]{4}$)[a-z]{1,2}[0-9]{2,3}

REGEX_1 [a-z0-9]{4}$ตรงกับตัวอักษรและตัวเลขสี่ตัวตามด้วยท้ายบรรทัด
REGEX_2 [a-z]{1,2}[0-9]{2,3}ตรงกับตัวอักษรหนึ่งหรือสองตัวตามด้วยตัวเลขสองหรือสามหลัก

REGEX_1 ตรวจสอบให้แน่ใจว่าความยาวของสตริงเป็น 4 จริง ๆ แต่ไม่ใช้อักขระใด ๆ เพื่อให้การค้นหา REGEX_2 เริ่มต้นที่ตำแหน่งเดียวกัน ตอนนี้ REGEX_2 ทำให้แน่ใจว่าสตริงตรงกับกฎอื่น ๆ หากไม่มีการมองล่วงหน้ามันจะจับคู่สายยาวสามหรือห้าเส้น

  • Lookahead เชิงลบ

ไวยากรณ์:

(?!REGEX_1)REGEX_2

จับคู่เฉพาะในกรณีที่ REGEX_1 ไม่ตรงกัน หลังจากตรวจสอบ REGEX_1 การค้นหา REGEX_2 จะเริ่มต้นที่ตำแหน่งเดียวกัน

ตัวอย่าง:

(?!.*\bFWORD\b)\w{10,30}$

ส่วนมองไปข้างหน้าตรวจสอบFWORDในในสตริงและล้มเหลวหากพบว่ามัน หากไม่พบFWORDการค้นหาล่วงหน้าจะสำเร็จและส่วนต่อไปนี้จะตรวจสอบว่าความยาวของสตริงอยู่ระหว่าง 10 ถึง 30 และมีเพียงอักขระคำเท่านั้นa-zA-Z0-9_

การมองด้านหลังนั้นคล้ายกับการมองไปข้างหน้า: มันแค่มองไปข้างหลังตำแหน่งเคอร์เซอร์ปัจจุบัน รสชาติของ regex บางอย่างเช่น javascript ไม่สนับสนุนการยืนยันที่อยู่เบื้องหลัง และรสชาติส่วนใหญ่ที่รองรับ (PHP, Python และอื่น ๆ ) ต้องการให้ส่วนที่ดูล้าหลังมีความยาวคงที่

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

ทำตามคำอธิบายของคุณดูเหมือนจะไม่ทำงานใน javascript, /(?=source)hello/.exec("source...hummhellosource ") = null คำอธิบายของคุณถูกต้องหรือไม่
Helin Wang

@HelinWang คำอธิบายนั้นถูกต้อง regex ของคุณคาดหวังสตริงที่เป็นทั้งแหล่งและสวัสดีในเวลาเดียวกัน!
Amarghosh

@jddxf สนใจที่จะทำอย่างละเอียด?
Amarghosh

@Amarghosh ฉันเห็นด้วยกับ "พวกเขาตรวจสอบ regex (ไปทางขวาหรือซ้ายของตำแหน่งปัจจุบัน - ขึ้นอยู่กับล่วงหน้าหรือหลัง) ประสบความสำเร็จหรือล้มเหลวเมื่อพบการแข่งขัน (ขึ้นอยู่กับว่ามันเป็นบวกหรือลบ) และทิ้งการจับคู่ ส่วน.". ดังนั้น lookahead ควรตรวจสอบ regex ทางด้านขวาของตำแหน่งปัจจุบันและไวยากรณ์ของ lookahead เชิงบวกควรเป็น x (? = y)
jddxf

@Amarghosh จะ(?=REGEX_1)REGEX_2เพียงตรงถ้าREGEX_2มาหลังจากที่ REGEX_1 ?
aandis

0

Grokking มองอย่างรวดเร็ว
วิธีแยกความแตกต่างระหว่าง lookahead และ lookbehind ใช้เวลาเดินทาง 2 นาทีกับฉัน:

(?=) - positive lookahead
(?<=) - positive lookbehind

สมมติ

    A  B  C #in a line

ตอนนี้เราถาม B คุณอยู่ไหน
B มีสองวิธีในการประกาศตำแหน่ง:

หนึ่ง, B มี A อยู่ข้างหน้าและมี C อยู่
สอง, B อยู่ข้างหน้า (lookahead) ของ C และข้างหลัง (lookhehind) A.

อย่างที่เราเห็นด้านหลังและข้างหน้าตรงข้ามกับสองวิธี
Regex เป็นทางออกที่สอง

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