อะไรคือความแตกต่างระหว่าง .*? และ. * นิพจน์ทั่วไป


142

ฉันพยายามแบ่งสตริงออกเป็นสองส่วนโดยใช้ regex สตริงถูกจัดรูปแบบดังนี้:

text to extract<number>

ฉันเคยใช้(.*?)<และใช้<(.*?)>งานได้ดี แต่หลังจากอ่านเป็น regex เล็กน้อยฉันเพิ่งเริ่มสงสัยว่าทำไมฉันต้องการ?ในการแสดงออก ฉันเพิ่งทำแบบนั้นหลังจากพบพวกเขาผ่านเว็บไซต์นี้ดังนั้นฉันไม่แน่ใจว่าสิ่งที่แตกต่างคืออะไร


คำตอบ:


172

มันเป็นความแตกต่างระหว่างปริมาณโลภและไม่โลภ

101000000000100พิจารณาการป้อนข้อมูล

การใช้1.*1, *เป็นโลภ - มันจะตรงกับทุกวิธีที่จะสิ้นสุดแล้ว BACKTRACK จนกว่าจะสามารถจับคู่ออกจากคุณกับ1 ไม่โลภ จะตรงกับอะไร แต่แล้วก็จะพยายามที่จะตรงกับตัวอักษรพิเศษจนกว่าจะตรงที่สุดที่ตรงกับ1010000000001
.*?*1101

ปริมาณทั้งหมดมีโหมดไม่โลภ: .*?, .+?, และแม้กระทั่ง.{2,6}?.??

ในกรณีของคุณรูปแบบที่คล้ายกันอาจเป็น<([^>]*)>- จับคู่สิ่งใดก็ได้ แต่มีเครื่องหมายมากกว่า (พูดอย่างเคร่งครัดมันตรงกับอักขระศูนย์หรือมากกว่านั้นนอกเหนือจากที่>อยู่ระหว่าง<และ>)

ดูปริมาณโกงแผ่น


อ่าฉันชอบอันสุดท้ายของทุกอย่างยกเว้นเครื่องหมาย>!
Doug

1
คุณช่วยอธิบายหรือแสดงตัวอย่างว่าโลภ?แตกต่างจากคนโลภได้??อย่างไร?
AdrianHHH

4
แน่ใจ สำหรับสตริง"abc"นั้น regex /\w\w?\w/จะจับคู่สตริงเต็ม"abc"- เพราะ?โลภมาก /\w\w??\w/เป็นคนขี้เกียจ - "ab"มันจะตรงกับ มันจะย้อนกลับและจับคู่"abc"หากล้มเหลวในภายหลัง
Kobi

184

เมื่อโลภเทียบกับไม่โลภ

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

ใน?ฐานะที่เป็นปริมาณการทำซ้ำการเปลี่ยนแปลงพฤติกรรมนี้เป็นที่ไม่โลภเรียกว่าลังเล ( ในเช่น Java ) (และบางครั้ง "ขี้เกียจ") ในทางกลับกันการทำซ้ำนี้จะพยายามจับคู่reps น้อยที่สุดเท่าที่จะเป็นไปได้ก่อนและเมื่อสิ่งนี้ไม่ทำงานและต้องย้อนรอยพวกเขาจะเริ่มจับคู่หนึ่งครั้งต่อครั้ง เป็นผลให้เมื่อการแข่งขันเกิดขึ้นในที่สุดการทำซ้ำอย่างไม่เต็มใจจะจับคู่ให้น้อยที่สุดเท่าที่จะทำได้

อ้างอิง


ตัวอย่างที่ 1: จาก A ถึง Z

ลองเปรียบเทียบทั้งสองรูปแบบ: และA.*ZA.*?Z

รับอินพุตต่อไปนี้:

eeeAiiZuuuuAoooZeeee

รูปแบบให้ผลลัพธ์ที่ตรงกันดังต่อไปนี้:

ก่อนอื่นเรามาดูกันว่าA.*Zจะทำอย่างไร เมื่อมันจับคู่แรกAแล้ว.*ความโลภจะพยายามจับคู่ให้ได้มาก.ที่สุดก่อน

eeeAiiZuuuuAoooZeeee
   \_______________/
    A.* matched, Z can't match

เนื่องจากZไม่ตรงกัน backtracks เครื่องยนต์และ.*ต้องตรงกันน้อยกว่าหนึ่ง.:

eeeAiiZuuuuAoooZeeee
   \______________/
    A.* matched, Z still can't match

สิ่งนี้เกิดขึ้นอีกสองสามครั้งจนในที่สุดเราก็มาถึงสิ่งนี้:

eeeAiiZuuuuAoooZeeee
   \__________/
    A.* matched, Z can now match

ตอนนี้Zสามารถจับคู่ได้ดังนั้นรูปแบบโดยรวมจึงตรงกัน:

eeeAiiZuuuuAoooZeeee
   \___________/
    A.*Z matched

ในทางตรงกันข้ามการทำซ้ำไม่เต็มใจในA.*?Zการแข่งขันครั้งแรกไม่กี่.ที่เป็นไปได้และจากนั้นการเพิ่มเติม.ตามความจำเป็น สิ่งนี้อธิบายได้ว่าเหตุใดจึงพบการแข่งขันสองรายการในอินพุต

ต่อไปนี้เป็นภาพที่แสดงให้เห็นถึงสิ่งที่ทั้งสองรูปแบบตรงกัน:

eeeAiiZuuuuAoooZeeee
   \__/r   \___/r      r = reluctant
    \____g____/        g = greedy

ตัวอย่าง: ทางเลือก

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

รูปแบบA[^Z]*Zยังพบการจับคู่สองแบบเดียวกันกับA.*?Zรูปแบบสำหรับอินพุตด้านบน ( ดังที่เห็นใน ideone.com ) [^Z]เป็นสิ่งที่เรียกว่าตัวละครคลาสเมื่อตะกี้ : มันตรงกับอะไร Zแต่

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

อ้างอิง


ตัวอย่างที่ 2: จาก A ถึง ZZ

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

eeAiiZooAuuZZeeeZZfff

นี่คือการจับคู่สำหรับอินพุตด้านบน:

นี่คือภาพที่แสดงให้เห็นถึงสิ่งที่พวกเขาจับคู่:

         ___n
        /   \              n = negated character class
eeAiiZooAuuZZeeeZZfff      r = reluctant
  \_________/r   /         g = greedy
   \____________/g

หัวข้อที่เกี่ยวข้อง

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

การทำซ้ำโลภครั้งหนึ่งอาจเกินความสามารถไปอีกอย่างหนึ่ง


1
ฉันหมายถึงการพูด rubular.com ไม่ใช่ ideone.com สำหรับผู้อื่น: อย่าแก้ไขโพสต์นี้ให้ฉันฉันจะทำเองในการแก้ไขครั้งถัดไปพร้อมกับตัวอย่างอื่น ๆ รู้สึกอิสระที่จะให้ข้อเสนอแนะข้อเสนอแนะและอื่น ๆ ในความคิดเห็นเพื่อให้ฉันสามารถรวมเหล่านั้นเช่นกัน
polygenelubricants

2
ดูเพิ่มเติมที่: stackoverflow.com/questions/3145023/…
polygenelubricants

4
คำตอบนี้ถูกเพิ่มไปยังคำถามที่พบบ่อยสแต็คล้นล้นปกติภายใต้ "ปริมาณ> เพิ่มเติมเกี่ยวกับความแตกต่าง ... "
aliteralmind

คำตอบนี้สมควรจะได้รับคำตอบที่เลือก! ขอบคุณมากสำหรับคำอธิบายโดยละเอียดของคุณ
masky007

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

20

สมมติว่าคุณมี:

<a></a>

<(.*)>จะตรงกับa></aที่เป็นจะตรงกับ<(.*?)> หยุดหลังหลังจากที่นัดแรกa >มันตรวจสอบหนึ่งหรือ 0 แมตช์.*แล้วตามด้วยนิพจน์ถัดไป

การแสดงออกครั้งแรกไม่ได้หยุดเมื่อจับคู่ครั้งแรก<(.*)> มันจะดำเนินต่อไปจนถึงนัดสุดท้ายของ>>


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