ขอบเขตของคำใน regex คืออะไร?


138

ฉันใช้ Java regexes ใน Java 1.6 (เพื่อแยกวิเคราะห์เอาต์พุตตัวเลขท่ามกลางวัตถุประสงค์อื่น ๆ ) และไม่สามารถหาคำจำกัดความที่แม่นยำของ\b("ขอบเขตคำ") ได้ ฉันเดาว่า-12น่าจะเป็น "คำจำนวนเต็ม" (จับคู่โดย \b\-?\d+\b) แต่ดูเหมือนว่าจะใช้ไม่ได้ ฉันจะขอบคุณที่ทราบวิธีจับคู่ตัวเลขที่คั่นด้วยช่องว่าง

ตัวอย่าง:

Pattern pattern = Pattern.compile("\\s*\\b\\-?\\d+\\s*");
String plus = " 12 ";
System.out.println(""+pattern.matcher(plus).matches());
String minus = " -12 ";
System.out.println(""+pattern.matcher(minus).matches());
pattern = Pattern.compile("\\s*\\-?\\d+\\s*");
System.out.println(""+pattern.matcher(minus).matches());

ผลตอบแทนนี้:

true
false
true

คุณสามารถโพสต์ตัวอย่างเล็ก ๆ ที่มีอินพุตและเอาต์พุตที่คาดหวังได้หรือไม่?
Brent Writes Code

ตัวอย่าง Pattern Pattern = Pattern.compile ("\\ s * \\ b \\ -? \\ d + \\ s *"); สตริงบวก = "12"; System.out.println ( "" + pattern.matcher (บวก) .matches ()); สตริงลบ = "-12"; System.out.println ( "" + pattern.matcher (ลบ) .matches ()); รูปแบบ = Pattern.compile ("\\ s * \\ -? \\ d + \\ s *"); System.out.println ( "" + pattern.matcher (ลบ) .matches ()); ให้: จริงเท็จจริง
peter.murray.rust

คำตอบ:


98

ขอบเขตคำในภาษา regex ส่วนใหญ่คือตำแหน่งระหว่าง\wและ\W(อักขระที่ไม่ใช่คำ) หรือที่จุดเริ่มต้นหรือจุดสิ้นสุดของสตริงหากขึ้นต้นหรือสิ้นสุด (ตามลำดับ) ด้วยอักขระคำ ( [0-9A-Za-z_])

ดังนั้นในสตริง"-12"จะจับคู่ก่อน 1 หรือหลัง 2 เส้นประไม่ใช่อักขระคำ


35
Correctamundo \bคือการยืนยันความกว้างเป็นศูนย์ที่จับคู่ว่ามี\wด้านใดด้านหนึ่งและด้าน\Wอื่น ๆ หรือตำแหน่งเป็นจุดเริ่มต้นหรือจุดสิ้นสุดของสตริง \wถูกกำหนดโดยพลการให้เป็นอักขระ "ตัวระบุ" (อัลนัมและขีดล่าง) ไม่ใช่สิ่งที่มีประโยชน์อย่างยิ่งสำหรับภาษาอังกฤษ
hobbs

ถูกต้อง 100% ขอโทษที่ไม่ได้แสดงความคิดเห็นกับคุณ ฉันกดส่งก่อนที่จะเห็นคำตอบของคุณ
Brent Writes Code

5
เพื่อประโยชน์ของความเข้าใจก็เป็นไปได้ที่จะเขียน regex ไม่\bhello\bโดยไม่ต้องใช้\b(ใช้\w, \Wและอื่น ๆ )?
David Portabella

5
เรียงลำดับจาก: (^|\W)hello($|\W)ยกเว้นว่าจะไม่จับอักขระที่ไม่ใช่คำก่อนและหลังดังนั้นจึงน่าจะเป็นมากกว่า(^|(?<=\W))hello($|(?=\W))(ใช้การยืนยัน lookahead / lookbehind)
brianary

7
@brianary ง่ายกว่าเล็กน้อย: (?<!\w)hello(?!\w).
David Knipe

28

ขอบเขตของคำสามารถเกิดขึ้นได้ในหนึ่งในสามตำแหน่ง:

  1. ก่อนอักขระตัวแรกในสตริงถ้าอักขระตัวแรกเป็นอักขระคำ
  2. หลังจากอักขระสุดท้ายในสตริงถ้าอักขระสุดท้ายเป็นอักขระคำ
  3. ระหว่างอักขระสองตัวในสตริงโดยที่ตัวหนึ่งเป็นอักขระคำและอีกตัวไม่ใช่อักขระคำ

อักขระคำเป็นตัวอักษรและตัวเลข เครื่องหมายลบไม่ใช่ ที่นำมาจากRegex กวดวิชา


21

ในหลักสูตรของการเรียนรู้การแสดงออกปกติผมก็ติดจริงๆใน metacharacter \bซึ่งเป็น ฉันไม่เข้าใจความหมายของมันในขณะที่ฉันกำลังถามตัวเองว่า " มันคืออะไรมันคืออะไร " ซ้ำแล้วซ้ำเล่า หลังจากใช้งานเว็บไซต์ไปสักระยะหนึ่งฉันก็ระวังขีดกลางแนวตั้งสีชมพูที่จุดเริ่มต้นของคำทุกคำและท้ายคำ ฉันเข้าใจว่ามันมีความหมายดีในตอนนั้น ก็ตอนนี้ว่าคำ (\w ) -boundary

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

ป้อนคำอธิบายภาพที่นี่


3
เว็บไซต์ที่ดีมากในการทำความเข้าใจขอบเขตของคำคืออะไรและการแข่งขันเกิดขึ้นได้อย่างไร
vsingh

2
โพสต์นี้สมควรได้รับเครดิตในการแสดงแทนการบอกต่อ รูปภาพมีค่าหนึ่งพันคำ
M_M

13

ขอบเขตของคำคือตำแหน่งที่นำหน้าด้วยอักขระคำและไม่ตามด้วยอักขระคำหนึ่งหรือตามด้วยอักขระคำและไม่นำหน้าด้วย


8

ฉันพูดคุยเกี่ยวกับสิ่งที่\bขอบเขต regex สไตล์เป็นจริงที่นี่

เรื่องราวสั้น ๆ ว่าพวกเขากำลังมีเงื่อนไข พฤติกรรมของพวกเขาขึ้นอยู่กับสิ่งที่อยู่ถัดจาก

# same as using a \b before:
(?(?=\w) (?<!\w)  | (?<!\W) )

# same as using a \b after:
(?(?<=\w) (?!\w)  | (?!\W)  )

บางครั้งนั่นไม่ใช่สิ่งที่คุณต้องการ ดูคำตอบอื่น ๆ ของฉันสำหรับรายละเอียด


8

ฉันอยากจะอธิบายคำตอบของอลันมัวร์

ขอบเขตคำคือตำแหน่งที่นำหน้าด้วยอักขระคำและไม่ตามด้วยอักขระคำหนึ่งหรือตามด้วยอักขระคำและไม่นำหน้าด้วย

สมมติว่าฉันมีสตริง "นี่คือคตันและเธอwesome" และฉันควรจะเปลี่ยนที่เกิดขึ้นทั้งหมด (s) ตัวอักษร 'a' เท่านั้นหากจดหมายฉบับนี้ที่มีอยู่ใน"เขตแดนของคำ"คือไม่ควรแทนที่ตัวอักษรใน 'cat'a

ดังนั้นฉันจะแสดง regex (ในPython ) เป็น

re.sub("\ba","e", myString.strip())// แทนที่aด้วยe

เพื่อให้การส่งออกจะได้รับนี้เป็นeคทีครั้งที่เธอของwesomeee


5

ฉันวิ่งเข้าไปในปัญหายิ่งเลวร้ายลงเมื่อค้นหาข้อความสำหรับคำพูดชอบ.NET, C++, และC# Cคุณคิดว่าโปรแกรมเมอร์คอมพิวเตอร์จะรู้ดีกว่าการตั้งชื่อภาษาที่ยากต่อการเขียนนิพจน์ทั่วไป

อย่างไรก็ตามนี่คือสิ่งที่ฉันค้นพบ (สรุปส่วนใหญ่มาจากhttp://www.regular-expressions.infoซึ่งเป็นเว็บไซต์ที่ยอดเยี่ยม): ใน regex ส่วนใหญ่อักขระที่จับคู่โดยคลาสอักขระมือสั้น\wคือ อักขระที่ถือว่าเป็นอักขระคำตามขอบเขตของคำ Java เป็นข้อยกเว้น Java รองรับ Unicode สำหรับ\bแต่ไม่ใช่สำหรับ\w. (ฉันแน่ใจว่ามีเหตุผลที่ดีในตอนนั้น)

\wย่อมาจากคำว่า "ตัวอักษร" มันจะตรงกับอักขระ ASCII [A-Za-z0-9_]เสมอ สังเกตการรวมขีดล่างและตัวเลข (แต่ไม่ใช่เส้นประ!) ในรสชาติส่วนใหญ่ที่รองรับ Unicode จะ\wมีอักขระจำนวนมากจากสคริปต์อื่น ๆ มีความไม่สอดคล้องกันมากเกี่ยวกับตัวละครที่รวมอยู่ โดยทั่วไปจะรวมตัวอักษรและตัวเลขจากสคริปต์ตัวอักษรและอุดมคติไว้ด้วย เครื่องหมายวรรคตอนของตัวเชื่อมต่อนอกเหนือจากเครื่องหมายขีดล่างและสัญลักษณ์ตัวเลขที่ไม่ใช่ตัวเลขอาจรวมหรือไม่มีก็ได้ XML Schema และ XPath ยังรวมสัญลักษณ์ทั้งหมดใน\w. แต่ Java, JavaScript และ PCRE จับคู่เฉพาะอักขระ ASCII ที่มี\w.

ซึ่งเป็นเหตุผลที่ Java-based ค้นหา regex สำหรับC++, C#หรือ.NET(แม้ในขณะที่คุณจำที่จะหลบหนีและระยะเวลา pluses) \bจะเมาโดย

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

อย่างไรก็ตามใน Java หากคุณกำลังค้นหาข้อความสำหรับภาษาชื่อแปลก ๆ เหล่านั้นคุณต้องแทนที่\bด้วยก่อนและหลังช่องว่างและตัวกำหนดเครื่องหมายวรรคตอน ตัวอย่างเช่น:

public static String grep(String regexp, String multiLineStringToSearch) {
    String result = "";
    String[] lines = multiLineStringToSearch.split("\\n");
    Pattern pattern = Pattern.compile(regexp);
    for (String line : lines) {
        Matcher matcher = pattern.matcher(line);
        if (matcher.find()) {
            result = result + "\n" + line;
        }
    }
    return result.trim();
}

จากนั้นในการทดสอบหรือฟังก์ชันหลักของคุณ:

    String beforeWord = "(\\s|\\.|\\,|\\!|\\?|\\(|\\)|\\'|\\\"|^)";   
    String afterWord =  "(\\s|\\.|\\,|\\!|\\?|\\(|\\)|\\'|\\\"|$)";
    text = "Programming in C, (C++) C#, Java, and .NET.";
    System.out.println("text="+text);
    // Here is where Java word boundaries do not work correctly on "cutesy" computer language names.  
    System.out.println("Bad word boundary can't find because of Java: grep with word boundary for .NET="+ grep("\\b\\.NET\\b", text));
    System.out.println("Should find: grep exactly for .NET="+ grep(beforeWord+"\\.NET"+afterWord, text));
    System.out.println("Bad word boundary can't find because of Java: grep with word boundary for C#="+ grep("\\bC#\\b", text));
    System.out.println("Should find: grep exactly for C#="+ grep("C#"+afterWord, text));
    System.out.println("Bad word boundary can't find because of Java:grep with word boundary for C++="+ grep("\\bC\\+\\+\\b", text));
    System.out.println("Should find: grep exactly for C++="+ grep(beforeWord+"C\\+\\+"+afterWord, text));

    System.out.println("Should find: grep with word boundary for Java="+ grep("\\bJava\\b", text));
    System.out.println("Should find: grep for case-insensitive java="+ grep("?i)\\bjava\\b", text));
    System.out.println("Should find: grep with word boundary for C="+ grep("\\bC\\b", text));  // Works Ok for this example, but see below
    // Because of the stupid too-short cutsey name, searches find stuff it shouldn't.
    text = "Worked on C&O (Chesapeake and Ohio) Canal when I was younger; more recently developed in Lisp.";
    System.out.println("text="+text);
    System.out.println("Bad word boundary because of C name: grep with word boundary for C="+ grep("\\bC\\b", text));
    System.out.println("Should be blank: grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));
    // Make sure the first and last cases work OK.

    text = "C is a language that should have been named differently.";
    System.out.println("text="+text);
    System.out.println("grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));

    text = "One language that should have been named differently is C";
    System.out.println("text="+text);
    System.out.println("grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));

    //Make sure we don't get false positives
    text = "The letter 'c' can be hard as in Cat, or soft as in Cindy. Computer languages should not require disambiguation (e.g. Ruby, Python vs. Fortran, Hadoop)";
    System.out.println("text="+text);
    System.out.println("Should be blank: grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));

ป.ล. ขอขอบคุณที่http://regexpal.com/โดยที่โลกของ regex จะน่าสังเวชมาก


ฉันพยายามพยายามที่จะเข้าใจว่าทำไมฉันถึงจับคู่ไม่ได้C#แต่ตอนนี้ชัดเจนขึ้นแล้ว
Mugoma J. Okomba

4

ตรวจสอบเอกสารเกี่ยวกับเงื่อนไขขอบเขต:

http://java.sun.com/docs/books/tutorial/essential/regex/bounds.html

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

public static void main(final String[] args)
    {
        String x = "I found the value -12 in my string.";
        System.err.println(Arrays.toString(x.split("\\b-?\\d+\\b")));
    }

เมื่อคุณพิมพ์ออกมาโปรดสังเกตว่าผลลัพธ์เป็นดังนี้:

[ฉันพบค่า - ในสตริงของฉัน]

ซึ่งหมายความว่าอักขระ "-" ไม่ได้ถูกเลือกว่าอยู่ในขอบเขตของคำเพราะไม่ถือว่าเป็นอักขระของคำ ดูเหมือนว่า @brianary จะเอาชนะฉันด้วยหมัดดังนั้นเขาจึงได้รับการโหวตเพิ่มขึ้น


2

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

--?\b\d+\b

ตรวจสอบการสาธิตการทำงาน


1

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

ทางเลือกหนึ่งที่เป็นไปได้คือ

(?:(?:^|\s)-?)\d+\b

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


0

ฉันคิดว่ามันเป็นขอบเขต (เช่นอักขระที่ตามหลัง) ของการแข่งขันครั้งสุดท้ายหรือจุดเริ่มต้นหรือจุดสิ้นสุดของสตริง


1
คุณกำลังนึกถึง\G: ตรงกับจุดเริ่มต้นของสตริง (เช่น\A) ในการแข่งขันครั้งแรก หลังจากนั้นจะตรงกับตำแหน่งที่การแข่งขันก่อนหน้าสิ้นสุดลง
Alan Moore

0

เมื่อคุณใช้\\b(\\w+)+\\bนั่นหมายถึงการจับคู่แบบตรงทั้งหมดกับคำที่มีอักขระคำเท่านั้น([a-zA-Z0-9])

ในกรณีของคุณตัวอย่างเช่นการตั้งค่า\\bที่จุดเริ่มต้นของ regex จะยอมรับ-12(พร้อมช่องว่าง) แต่อีกครั้งจะไม่ยอมรับ-12(ไม่มีช่องว่าง)

สำหรับการอ้างอิงเพื่อสนับสนุนคำพูดของฉัน: https://docs.oracle.com/javase/tutorial/essential/regex/bounds.html

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