'สันหลังยาว' และ 'โลภ' หมายถึงอะไรในบริบทของการแสดงออกปกติ?


คำตอบ:


644

โลภจะกินมากที่สุด จากhttp://www.regular-expressions.info/repeat.html<.+>เราเห็นตัวอย่างของการพยายามที่จะตรงกับแท็กด้วย สมมติว่าคุณมีสิ่งต่อไปนี้:

<em>Hello World</em>

คุณอาจคิดว่า<.+>( .หมายถึงตัวอักษรใด ๆ ที่ไม่ใช่การขึ้นบรรทัดใหม่และ+วิธีการอย่างใดอย่างหนึ่งหรือมากกว่า ) เท่านั้นที่จะตรงกับ<em>และ</em>เมื่อในความเป็นจริงมันจะโลภมากและไปจากครั้งแรกไปยังหน้าล่าสุด< >ซึ่งหมายความว่าจะจับคู่<em>Hello World</em>แทนที่จะเป็นสิ่งที่คุณต้องการ

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

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


2
ถ้าคุณใช้โลภคุณจะมี 3 การแข่งขัน (1 องค์ประกอบ + 2 แท็ก) หรือเพียงแค่ 1 การแข่งขัน (1 องค์ประกอบ)?
ajsie

10
มันจะตรงกับเวลาเพียง 1 เริ่มจากคนแรก<และลงท้ายด้วยสุดท้าย>
Sampson

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

อีกหนึ่งเครื่องมือที่ยอดเยี่ยมที่ฉันใช้เสมอ: debuggex.comนอกจากนี้ยังมีฟังก์ชั่น "ฝังบน StackOverflow"
Ron van der Heijden

8
เพียงแค่เพิ่มว่ามีวิธีโลภที่จะไปเกี่ยวกับเรื่องนี้เช่นกัน: <[^>]+> regex101.com/r/lW0cY6/1
alanbuchanan

302

'โลภ'หมายถึงการจับคู่สตริงที่ยาวที่สุดที่เป็นไปได้

'Lazy'หมายถึงจับคู่สตริงที่สั้นที่สุด

ยกตัวอย่างเช่นโลภh.+lการแข่งขัน'hell'ใน'hello'แต่ขี้เกียจที่ตรงกันh.+?l'hel'


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

3
สำหรับทุกคนที่อ่านโพสต์: โลภหรือขี้เกียจเครื่องวัดปริมาณด้วยตัวเองจะไม่ตรงกับสตริงย่อยที่ยาวที่สุด / สั้นที่สุด คุณจะต้องใช้โทเค็นโลภอารมณ์หรือใช้วิธีที่ไม่ใช่ regex
Wiktor Stribiżew

3
@AndrewS อย่าสับสนโดย double ll ในตัวอย่าง มันค่อนข้างขี้เกียจที่จะจับคู่ซับสตริงที่สั้นที่สุดที่เป็นไปได้ในขณะที่โลภจะตรงกับที่ยาวที่สุด โลภh.+lการแข่งขัน'helol'ใน'helolo'แต่ขี้เกียจที่ตรงกันh.+?l 'hel'
v.shashenko

3
@FloatingRock: ไม่ได้x?หมายความว่าxเป็นทางเลือก แต่+?เป็นไวยากรณ์ที่แตกต่างกัน หมายถึงหยุดมองหาสิ่งที่ตรงกัน - จับคู่ขี้เกียจ
slebetman

1
@FloatingRock: สำหรับวิธีที่คุณแยกความแตกต่างไวยากรณ์ที่แตกต่างกันง่าย: ?หมายถึงตัวเลือกและ+?หมายถึงสันหลังยาว ดังนั้น\+?หมายถึง+เป็นตัวเลือก
slebetman

113
+-------------------+-----------------+------------------------------+
| Greedy quantifier | Lazy quantifier |        Description           |
+-------------------+-----------------+------------------------------+
| *                 | *?              | Star Quantifier: 0 or more   |
| +                 | +?              | Plus Quantifier: 1 or more   |
| ?                 | ??              | Optional Quantifier: 0 or 1  |
| {n}               | {n}?            | Quantifier: exactly n        |
| {n,}              | {n,}?           | Quantifier: n or more        |
| {n,m}             | {n,m}?          | Quantifier: between n and m  |
+-------------------+-----------------+------------------------------+

เพิ่ม ? เพื่อ quantifier เพื่อให้ ungreedy ie ขี้เกียจ

: ตัวอย่าง
สตริงทดสอบ: StackOverflow
แสดงออก reg โลภ : s.*oเอาท์พุท: stackoverflo W
แสดงออก reg ขี้เกียจ : s.*?oเอาท์พุท: Stacko verflow


2
ไม่ใช่ ?? เทียบเท่ากับ? . ในทำนองเดียวกันไม่ใช่ {n} หรือ เทียบเท่ากับ {n}
หมายเลข 945

5
@BreakingBenjamin: ไม่ ?? ไม่เท่ากับหรือไม่? เมื่อมีตัวเลือกให้ส่งคืนค่า 0 หรือ 1 ครั้งจะเลือก 0 (ขี้เกียจ) แทน เห็นความแตกต่างเกิดผลการเปรียบเทียบที่จะre.match('(f)?(.*)', 'food').groups() re.match('(f)??(.*)', 'food').groups()ในหลัง(f)??จะไม่ตรงกับผู้นำ 'f' แม้ว่าจะทำได้ ดังนั้น 'f' จะถูกจับคู่โดยกลุ่มการจับภาพที่สอง '. *' ฉันแน่ใจว่าคุณสามารถสร้างตัวอย่างด้วย '{n}?' เกินไป. เป็นที่ยอมรับกันว่าทั้งสองนี้ไม่ค่อยได้ใช้กันมากนัก
smci

55

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

abcdefghijklmc

และการแสดงออกนี้:

a.*c

abcการแข่งขันโลภจะตรงกับสตริงทั้งหมดและการแข่งขันขี้เกียจจะตรงกับเพียงแค่ครั้งแรก


16

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

ในฐานะ @Andre S ที่กล่าวถึงในความคิดเห็น

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

อ้างถึงตัวอย่างด้านล่างสำหรับสิ่งที่โลภและสิ่งที่ขี้เกียจ

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test {
    public static void main(String args[]){
        String money = "100000000999";
        String greedyRegex = "100(0*)";
        Pattern pattern = Pattern.compile(greedyRegex);
        Matcher matcher = pattern.matcher(money);
        while(matcher.find()){
            System.out.println("I'm greeedy and I want " + matcher.group() + " dollars. This is the most I can get.");
        }

        String lazyRegex = "100(0*?)";
        pattern = Pattern.compile(lazyRegex);
        matcher = pattern.matcher(money);
        while(matcher.find()){
            System.out.println("I'm too lazy to get so much money, only " + matcher.group() + " dollars is enough for me");
        }
    }
}


ผลลัพธ์คือ:

I'm greeedy and I want 100000000 dollars. This is the most I can get.

I'm too lazy to get so much money, only 100 dollars is enough for me

9

นำมาจากwww.regular-expressions.info

ความโลภ: ตัวนับปริมาณโลภพยายามที่จะทำซ้ำโทเค็นซ้ำหลาย ๆ ครั้งที่เป็นไปได้และค่อย ๆ ยกเลิกการแข่งขันในขณะที่เครื่องยนต์ backtracks เพื่อค้นหาการแข่งขันโดยรวม

Laziness : Lazy quantifier จะทำซ้ำโทเค็นซ้ำสองสามครั้งตามที่ต้องการและค่อย ๆ ขยายการจับคู่เป็น backtracks เครื่องยนต์ผ่าน regex เพื่อค้นหาการจับคู่โดยรวม


6

จากนิพจน์ทั่วไป

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

โดยใช้ตัวบ่งชี้ที่ขี้เกียจนิพจน์จะพยายามจับคู่ที่น้อยที่สุดก่อน


4

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

ตัวอย่าง:

import re
text = "<body>Regex Greedy Matching Example </body>"
re.findall('<.*>', text)
#> ['<body>Regex Greedy Matching Example </body>']

แทนที่จะจับคู่จนถึงเหตุการณ์แรกของ '>' มันจะดึงสตริงทั้งหมด นี่คือความโลภเริ่มต้นหรือ 'เอามันทั้งหมด' พฤติกรรมของ regex

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

ตัวอย่าง:

re.findall('<.*?>', text)
#> ['<body>', '</body>']

หากคุณต้องการเรียกคืนเฉพาะนัดแรกให้ใช้วิธีการค้นหาแทน

re.search('<.*?>', text).group()
#> '<body>'

ที่มา: ตัวอย่าง Python Regex


3

โลภหมายความว่ามันจะใช้รูปแบบของคุณจนกว่าจะไม่มีใครเหลืออยู่

Lazy จะหยุดทันทีที่พบรูปแบบแรกที่คุณขอ

ตัวอย่างทั่วไปหนึ่งที่ฉันมักพบเป็น\s*-\s*?ของ regex([0-9]{2}\s*-\s*?[0-9]{7})

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


3

ดีที่สุดแสดงโดยตัวอย่าง เชือก 192.168.1.1และ regex โลภ\b.+\b คุณอาจคิดว่าสิ่งนี้จะให้อ็อกเท็ตที่ 1 แต่ตรงกับสตริงทั้งหมด ทำไม? เนื่องจาก. + เป็นโลภและจับคู่โลภตรงกับตัวละครทุกตัวใน192.168.1.1จนกว่าจะถึงจุดสิ้นสุดของสตริง นี่เป็นบิตที่สำคัญ! ตอนนี้มันจะเริ่มย้อนรอยทีละตัวอักษรจนกว่าจะพบการแข่งขันสำหรับโทเค็นที่ 3 ( \b)

หากสตริงไฟล์ข้อความ 4GB และ 192.168.1.1 เป็นจุดเริ่มต้นคุณสามารถเห็นได้อย่างง่ายดายว่าการย้อนรอยนี้จะทำให้เกิดปัญหาได้อย่างไร

หากต้องการทำให้ regex non greedy (สันหลังยาว) ให้ใส่เครื่องหมายคำถามหลังจากการค้นหาโลภของคุณเช่น

*?
??
+?

สิ่งที่เกิดขึ้นตอนนี้คือโทเค็น 2 ( +?) พบการแข่งขัน regex เคลื่อนที่ไปตามตัวละครแล้วลองใช้โทเค็นถัดไป ( \b) แทนโทเค็น 2 ( +?) ดังนั้นมันจึงเล็ดลอดไปด้วยขิง


0

Quantifier โลภเป็นเหมือน IRS / ATO: พวกเขาใช้เวลาให้มากที่สุด:

หากอยู่ที่นั่นพวกเขาจะมารับ พวกเขาจะเอามันทั้งหมด:

ตัวอย่างเช่น IRS จับคู่กับ regex นี้: .*

$50,000 - กรมสรรพากรจะทำทุกอย่าง ผู้โลภ.*{4}?ERS

ดูที่นี่สำหรับตัวอย่าง: regexr.com/4t27f

ปริมาณที่ไม่โลภ - พวกเขาใช้เวลาน้อยที่สุดเท่าที่จะทำได้

ในทางกลับกันถ้าฉันขอคืนภาษี IRS ก็กลายเป็นไม่โลภและพวกเขาใช้ตัวนับนี้:

(.{2}?)([0-9]*)กับนิพจน์นี้: $50,000กลุ่มแรกไม่ตรงกับความต้องการและตรงเท่านั้น$5- ดังนั้นฉันจึงได้รับ$5เงินคืน ส่วนที่เหลือถูกลุงแซมนำไปใช้อย่างสิ้นเปลือง

ดูที่นี่: ไม่โลภ-ตัวอย่างเช่น

ทำไมต้องรำคาญ

มันสำคัญถ้าคุณพยายามจับคู่บางส่วนของนิพจน์ บางครั้งคุณไม่ต้องการจับคู่ทุกอย่าง


-3

พยายามเข้าใจพฤติกรรมดังต่อไปนี้:

    var input = "0014.2";

Regex r1 = new Regex("\\d+.{0,1}\\d+");
Regex r2 = new Regex("\\d*.{0,1}\\d*");

Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // "0014.2"

input = " 0014.2";

Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // " 0014"

input = "  0014.2";

Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // ""
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.