นิพจน์ปกติ: มีตัวดำเนินการ AND หรือไม่


708

เห็นได้ชัดว่าคุณสามารถใช้|(ท่อ?) เพื่อเป็นตัวแทนORแต่มีวิธีที่จะเป็นตัวแทนANDเช่นกัน?

โดยเฉพาะฉันต้องการจับคู่ย่อหน้าข้อความที่มีวลีทั้งหมด แต่ไม่เรียงตามลำดับ


1
คุณหมายถึงว่าคุณต้องการค้นหาวลีในข้อความหรือไม่ซึ่งแต่ละวลีดังกล่าวเป็นการเปลี่ยนแปลงคำที่ถูกต้องในวลีที่กำหนด
Nietzche-jou

2
ฉันวางมันไว้ที่นี่เพราะคำตอบสามหรือสี่ข้อไม่สนใจ Lookahead ไม่ตรงกับความยาวเท่ากันสำหรับแต่ละประโยคยกเว้นว่าลงท้ายด้วย $ หนึ่ง lookahead สามารถจับคู่อักขระสี่ตัวและอีก 6 ตัวอย่างเช่น (? = a *) (? = aab) จะจับคู่ aabaaaaba
Zachary Vance

2
ลองใช้อักขระ "space" สำหรับตัวดำเนินการ "AND"

1 I'd like to match paragraphs of text. 2. มีส่วนผสมของออกจากการสั่งซื้อข้อความ หมายเลข 1 เปิดให้ตีความ หมายเลข 2 สามารถทำได้สองวิธี วิธีที่ 1: (?:(?:(?(1)(?!))\b(phrase1)\b.*?|(?(2)(?!))\b(phrase2)\b.*?)){2}วิธีที่ 2: (?=.*\bphrase1\b)(?=.*\bphrase2\b)ในที่นี้การจับคู่ของย่อหน้าในกรณีนี้จะไม่ได้กำหนดจนกว่าจะมีการกำหนดคำนิยามของวรรคอย่างเป็นทางการ

คำตอบ:


385

ใช้นิพจน์ทั่วไปที่ไม่เสียเวลา

สัญกรณ์ทั่วไป (เช่น Perl / Java) คือ:

(?=expr)

ซึ่งหมายความว่า "จับคู่exprแต่หลังจากนั้นจับคู่ต่อที่จุดเดิม"

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

(?=match this expression)(?=match this too)(?=oh, and this)

คุณสามารถเพิ่มกลุ่มการดักจับภายในนิพจน์ที่ไม่ต้องเสียเวลาหากคุณต้องการบันทึกข้อมูลบางส่วนในนั้น


3
perl -e "q {บางสิ่งและบางสิ่งบางอย่าง} = ~ / (? = บางอย่าง) (? = สิ่งที่) (? = สิ่งที่) /? พิมพ์ 'ใช่': พิมพ์ 'no'" พิมพ์ 'no'
Robert P

27
ควรกล่าวถึงตัวอย่างนี้โดยเฉพาะเรียกว่าการยืนยันในเชิงบวก (lookahead) มันมีประโยชน์อื่น ๆ กว่า "และ" โปรดทราบว่าข้อความไม่ถูกใช้
แปลกหน้า

7
ใช้ (? =) เช่นนี้ผลลัพธ์ใน regex ที่ไม่เคยประสบความสำเร็จ แต่มันเป็นการเชื่อมต่อแบบแอนะล็อกกับ | OP เป็นเพียงความผิดในสิ่งที่เขาคิดว่าจะแก้ปัญหาของเขา
Nietzche-jou

10
perl -e "q {บางสิ่งและสิ่งของ} = ~ /(?=.*some)(?=.*stuff)(?=.*things)/? พิมพ์ 'ใช่': พิมพ์ 'ไม่'"
kriss

3
คุณช่วยเพิ่มตัวอย่างง่ายๆในรหัส Perl ในคำตอบของคุณได้ไหม
Pithikos

343

คุณต้องใช้ lookahead ตามที่ผู้ตอบคนอื่นได้กล่าวไว้ แต่ lookahead ต้องคำนึงถึงอักขระอื่น ๆ ระหว่างคำเป้าหมายและตำแหน่งการแข่งขันปัจจุบัน ตัวอย่างเช่น:

(?=.*word1)(?=.*word2)(?=.*word3)

.*ใน lookahead แรกช่วยให้มันตรงกับตัวละครมาก แต่ก็ต้องก่อนที่จะได้รับการ "word1" จากนั้นตำแหน่งการแข่งขันจะถูกรีเซ็ตและ lookahead ที่สองจะค้นหา "word2" รีเซ็ตอีกครั้งและส่วนสุดท้ายตรงกับ "word3"; เนื่องจากเป็นคำสุดท้ายที่คุณกำลังตรวจสอบดังนั้นจึงไม่จำเป็นว่าต้องอยู่ในสถานะ lookahead แต่ไม่เจ็บ

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

/^(?=.*word1)(?=.*word2)(?=.*word3).*$/m

ตัวปรับ 'm' ใช้สำหรับโหมดหลายบรรทัด มันช่วยให้^และ$จับคู่ที่ขอบเขตย่อหน้า ("ขอบเขตของเส้น" ใน regex-speak) เป็นสิ่งสำคัญในกรณีนี้ที่คุณไม่ต้องใช้ตัวปรับ 's' ซึ่งจะทำให้ dot metacharacter จับคู่บรรทัดใหม่และอักขระอื่น ๆ ทั้งหมด

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

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m

8
ถูกต้องแน่นอน - มีการสอนเกี่ยวกับเรื่องนี้ด้วย! ocpsoft.org/tutorials/regular-expressions/and-in-regex
ลินคอล์น

9
ขอบคุณมาก * สิ่งนี้สร้างความแตกต่างได้
Gennadiy Ryabkin

1
+1 สำหรับคำตอบที่ชัดเจนและชัดเจนแสดงให้เห็นถึงหนึ่งในการใช้งานที่ดีที่สุดสำหรับ lookaheads (ต่างจากการใช้งานอย่างแฮ็กเพื่อนับเปอร์เซ็นต์การจับคู่ของรหัสผ่าน) :)
zx81

1
@Liam :. MySQL ใช้รสชาติ POSIX ERE ดังนั้นไม่ มันเสียสละคุณลักษณะในความโปรดปรานของประสิทธิภาพซึ่งดูเหมือนว่าสมเหตุสมผลสำหรับฉัน มีข้อมูลเพิ่มเติมเป็นที่นี่
อลันมัวร์

3
แทนที่.*ด้วย[\s\S]*ใน javascript หากคุณมีบรรทัดใหม่เช่นเดียวกับ.ในโปรแกรม regex ของ javascript ไม่ตรงกับบรรทัดใหม่และไม่สามารถทำการปรับเปลี่ยนได้
Wesley Smith

41

ดูตัวอย่างนี้:

เรามี 2 regexps A และ B และเราต้องการจับคู่ทั้งคู่ดังนั้นในรหัสหลอกดูเหมือนว่านี้:

pattern = "/A AND B/"

สามารถเขียนได้โดยไม่ใช้ตัวดำเนินการ AND ดังนี้:

pattern = "/NOT (NOT A OR NOT B)/"

ใน PCRE:

"/(^(^A|^B))/"

regexp_match(pattern,data)

24
นั่นเป็นความจริงในแง่ของตรรกะอย่างเป็นทางการ แต่มันไม่มีทางช่วยที่นี่ ใน regexes ไม่สามารถแสดงได้ยากกว่า AND
อลันมัวร์

@marvin_dpr มันทำงานให้ฉันใน CMake ในขณะที่ข้อเสนอแนะอื่น(?=expr)ไม่ได้ ดูเหมือนว่าจะขึ้นอยู่กับการใช้งาน
Melebius

38
ไม่ได้^หมายความว่า "การเริ่มต้นของสตริง" ในไวยากรณ์ของ regex ใช่หรือไม่
แลมบ์ดา Fairy

3
โดยทั่วไปแล้ว regex ^เป็นการปฏิเสธเฉพาะตอนต้นของคลาสอักขระเท่านั้น เว้นแต่ CMake กำลังทำสิ่งที่ขี้ขลาดจริงๆ (จนถึงจุดที่เรียกรูปแบบการจับคู่ภาษา "regex" ของพวกเขาอาจถือได้ว่าเป็นความเข้าใจผิดหรือไม่ถูกต้อง) ฉันเดาว่าข้อเท็จจริงที่ได้ผลสำหรับคุณนั้นเป็นอุบัติเหตุที่แยกจากกัน
tripleee

29

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

คุณสามารถระบุการเรียงสับเปลี่ยนที่เป็นไปได้ทั้งหมดด้วย regexp มาตรฐานเช่นนี้ (ตรงกับ a, b และ c ในลำดับใด ๆ ):

(abc)|(bca)|(acb)|(bac)|(cab)|(cba)

อย่างไรก็ตามสิ่งนี้ทำให้ regexp ยาวมากและอาจไม่มีประสิทธิภาพหากคุณมีมากกว่าสองคำ

หากคุณกำลังใช้เวอร์ชันขยายเพิ่มเติมบางอย่างเช่น Perl หรือ Java พวกเขามีวิธีที่ดีกว่าในการทำเช่นนี้ คำตอบอื่น ๆ มีข้อเสนอแนะโดยใช้การดำเนินงาน lookahead ในเชิงบวก


10
ฉันไม่คิดว่าวิธีการของคุณจะไร้ประสิทธิภาพมากกว่า 3 ลุคอะแฮดที่มีการย้อนรอยหายนะ แน่นอนว่ามันใช้เวลาเขียนนานกว่า แต่โปรดทราบว่าคุณสามารถสร้างรูปแบบโดยอัตโนมัติได้อย่างง่ายดาย a(bc|cb)|b(ac|ca)|c(ab|ba)โปรดทราบว่าคุณสามารถปรับปรุงได้ที่จะล้มเหลวได้รวดเร็วขึ้นด้วย และที่สำคัญที่สุดคุณสามารถใช้กับรสชาติของ regex ทั้งหมด
Casimir et Hippolyte

27

ผู้ประกอบการและเป็นนัยในไวยากรณ์นิพจน์ทั่วไป
ตัวดำเนินการ OR ต้องถูกระบุด้วยไพพ์
RegExp ต่อไปนี้:

var re = /ab/;

หมายถึงตัวอักษรa และbตัวอักษร
นอกจากนี้ยังทำงานกับกลุ่ม:

var re = /(co)(de)/;

มันหมายถึงกลุ่มco และdeกลุ่ม
การแทนที่ (โดยนัย) และด้วยหรือจะต้องมีบรรทัดต่อไปนี้:

var re = /a|b/;
var re = /(co)|(de)/;

29
น่าเสียดายที่นี่ไม่ใช่สิ่งที่ OP ร้องขอ สิ่งนี้พบสิ่งใดในลำดับนั้นในขณะที่พวกเขาต้องการในลำดับใด ๆ ตรวจสอบคำตอบโดยstackoverflow.com/users/20938/alan-mooreด้านล่างซึ่งเป็นคำตอบที่ถูกต้อง
JESii

1
@JESii ขอบคุณสำหรับจุดของคุณคุณพูดถูกและฉันเข้าใจผิดคำถามจาก Hugoware ฉันมุ่งเน้นเฉพาะประโยคแรกของเขา คำตอบที่ถูกต้องคือการใช้ตัวดำเนินการ lookahead อย่างเหมาะสมดังที่ AlanMoore เขียน อย่างไรก็ตามฉันคิดว่าบางคนอาจพบว่าการชี้แจงของฉันมีประโยชน์ตามที่ได้รับการอัปเดตแล้วดังนั้นฉันจะไม่ทิ้งทุกสิ่ง ความนับถือ.
Emanuele Del Grande

13

เป็นไปไม่ได้ในกรณีของคุณที่จะทำและในผลลัพธ์ที่ตรงกันหลาย ๆ ใน pseudocode

regexp_match(pattern1, data) && regexp_match(pattern2, data) && ...

3
ฉันอยู่ในสถานการณ์ที่ฉันมีรหัสบางส่วนที่เป็นตารางข้อมูลของกฎพร้อมกับสตริงการจับคู่รูปแบบ regex เดียวเพื่อทดสอบความถูกต้องของกฎ การย้ายไปยังการทดสอบหลายครั้งไม่ใช่สิ่งที่ฉันสามารถทำได้ในกรณีของฉันและโดยทั่วไปในกรณีของคนอื่นด้วย!
Alan Wolfe

11

ทำไมไม่ใช้ awk?
ด้วย awk regex และหรือเรื่องนั้นง่ายมาก

awk '/WORD1/ && /WORD2/ && /WORD3/' myfile

9

หากคุณใช้นิพจน์ปกติของ Perl คุณสามารถใช้ lookahead เชิงบวกได้

ตัวอย่างเช่น

(?=[1-9][0-9]{2})[0-9]*[05]\b

จะเป็นตัวเลขที่มากกว่า 100 และหารด้วย 5


8

คุณสามารถไพพ์เอาต์พุตของคุณไปยัง regex อื่น เมื่อใช้ grep คุณสามารถทำสิ่งนี้ได้:

grep A | grep B


8

นอกจากคำตอบที่ยอมรับแล้ว

ฉันจะให้ตัวอย่างที่เป็นประโยชน์แก่คุณซึ่งจะทำให้ชัดเจนมากขึ้นสำหรับคุณบางคน ตัวอย่างเช่นสมมติว่าเรามีข้อความสามบรรทัด:

[12/Oct/2015:00:37:29 +0200] // only this + will get selected
[12/Oct/2015:00:37:x9 +0200]
[12/Oct/2015:00:37:29 +020x]

ดูตัวอย่างได้ที่นี่ DEMO

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

'~(?<=\d{2} )\+(?=\d{4})~g'

หมายเหตุหากคุณแยกการแสดงออกมันจะให้ผลลัพธ์ที่แตกต่าง

หรือบางทีคุณต้องการเลือกข้อความระหว่างแท็ก ... แต่ไม่ใช่แท็ก! จากนั้นคุณสามารถใช้:

'~(?<=<p>).*?(?=<\/p>)~g'

สำหรับข้อความนี้:

<p>Hello !</p> <p>I wont select tags! Only text with in</p> 

ดูตัวอย่างได้ที่นี่ DEMO


คำตอบไหนเป็นคำตอบที่ยอมรับ กรุณาเพิ่มลิงค์ไปในอนาคตฉัน
James Brown

6

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

สิ่งที่คุณต้องการทำไม่สามารถทำได้ด้วย regexp เดียว


มันไม่ได้เป็นไปไม่ได้ในทางเทคนิค แต่ไม่คุ้มค่าที่จะใช้ ผมไม่รู้ว่าทำไมคน downvoted แต่ ...
โรเบิร์ต P

13
อาจเป็นเพราะไม่เพียง แต่เป็นไปได้ง่ายสมมติว่ารสชาติของคุณ regex รองรับ lookaheads และนั่นเป็นทางออกที่ดี ภาษาการเขียนโปรแกรมที่สำคัญส่วนใหญ่ในปัจจุบันสนับสนุน
Alan Moore

3

ใช้และนอกการแสดงออกปกติ ในผู้ประกอบการ lookahead PHP ดูเหมือนจะไม่ทำงานสำหรับฉันแทนฉันใช้มัน

if( preg_match("/^.{3,}$/",$pass1) && !preg_match("/\s{1}/",$pass1))
    return true;
else
    return false;

regex ด้านบนจะจับคู่หากความยาวรหัสผ่านคือ 3 ตัวอักษรขึ้นไปและไม่มีช่องว่างในรหัสผ่าน

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