JavaRush /จาวาบล็อก /Random-TH /นิพจน์ทั่วไปใน Java ตอนที่ 3

นิพจน์ทั่วไปใน Java ตอนที่ 3

เผยแพร่ในกลุ่ม
เราขอนำเสนอคำแปลคำแนะนำสั้นๆ เกี่ยวกับนิพจน์ทั่วไปในภาษา Java ซึ่งเขียนโดย Jeff Friesen สำหรับเว็บไซต์javaworld เพื่อความสะดวกในการอ่าน เราได้แบ่งบทความออกเป็นหลายส่วน นิพจน์ทั่วไปใน Java ตอนที่ 3 - 1นิพจน์ทั่วไปใน Java ส่วนที่ 1 นิพจน์ทั่วไปใน Java ส่วนที่ 2

ลดความซับซ้อนของงานการเขียนโปรแกรมทั่วไปด้วย Regex API

ในส่วนที่ 1 และ 2 ของบทความนี้ คุณได้รู้จักกับนิพจน์ทั่วไปและ Regex API คุณได้เรียนรู้เกี่ยวกับชั้นเรียนPatternและได้ดูตัวอย่างที่สาธิตการสร้างนิพจน์ทั่วไป ตั้งแต่การจับคู่รูปแบบอย่างง่ายโดยใช้สตริงตามตัวอักษรไปจนถึงการจับคู่ที่ซับซ้อนมากขึ้นโดยใช้ช่วง ตัวจับคู่ขอบเขต และตัวระบุปริมาณ ในส่วนนี้และส่วน ต่อๆ ไป เราจะพิจารณาประเด็นต่างๆ ที่ไม่ครอบคลุมในส่วนแรก เราจะศึกษาวิธีการที่เกี่ยวข้องของชั้นเรียนPatternและ นอกจากนี้คุณยังจะได้เรียนรู้ยูทิลิตี้สองตัวที่ใช้นิพจน์ทั่วไปเพื่อทำให้ปัญหาการเขียนโปรแกรมทั่วไปง่ายขึ้น อันแรกแยกความคิดเห็นจากโค้ดสำหรับเอกสารประกอบ ส่วนที่สองคือไลบรารีของโค้ดที่นำมาใช้ซ้ำได้ ซึ่งออกแบบมาเพื่อดำเนินการวิเคราะห์คำศัพท์ ซึ่งเป็นองค์ประกอบสำคัญของแอสเซมเบลอร์ คอมไพเลอร์ และซอฟต์แวร์ที่คล้ายกัน MatcherPatternSyntaxException

กำลังดาวน์โหลดซอร์สโค้ด

คุณสามารถรับซอร์สโค้ดทั้งหมด (สร้างโดย Jeff Friesen สำหรับ JavaWorld) สำหรับแอปพลิเคชันสาธิตในบทความนี้ได้จากที่นี่

การเรียนรู้ Regex API

PatternและMatcherเป็นPatternSyntaxExceptionสามคลาสที่ประกอบกันเป็น Regex API แต่ละวิธีมีวิธีการที่ช่วยให้คุณใช้นิพจน์ทั่วไปในโค้ดของคุณได้

วิธีการเรียนรูปแบบ

อินสแตนซ์ของคลาสPatternคือนิพจน์ทั่วไปที่คอมไพล์แล้ว หรือที่เรียกว่ารูปแบบ นิพจน์ทั่วไปได้รับการคอมไพล์เพื่อปรับปรุงประสิทธิภาพของการดำเนินการจับคู่รูปแบบ วิธีการคงที่ต่อไปนี้รองรับการคอมไพล์
  • Pattern compile(String regex)รวบรวมเนื้อหาregexเป็นตัวแทนระดับกลางซึ่งจัดเก็บไว้ในไฟล์Pattern. เมธอดนี้จะส่งคืนการอ้างอิงไปยังออบเจ็กต์หากสำเร็จ หรือส่งข้อยกเว้นPatternSyntaxExceptionหากตรวจพบไวยากรณ์ของนิพจน์ทั่วไปที่ไม่ถูกต้อง อ็อบเจ็กต์ใดๆ ของคลาสMatcherที่ใช้Patternหรือส่งคืนจากอ็อบเจ็กต์นี้จะใช้การตั้งค่าเริ่มต้น เช่น การค้นหาแบบคำนึงถึงขนาดตัวพิมพ์ ตามตัวอย่าง ข้อมูลโค้ดPattern p = Pattern.compile("(?m)^\\."); จะสร้างออบเจ็กต์Patternที่จัดเก็บการแสดงนิพจน์ทั่วไปที่คอมไพล์แล้วเพื่อจับคู่สตริงที่ขึ้นต้นด้วยอักขระจุด

  • Pattern compile(String regex, int flags)แก้ปัญหาเดียวกันกับPattern compile(String regex)แต่คำนึงถึงflags: ชุดของค่าคงที่บิตสำหรับแฟล็กบิตของประเภท OR คลาสPatternประกาศค่าคงที่ที่CANON_EQ, CASE_INSENSITIVE, COMMENTS, DOTALL, LITERAL, MULTILINE, UNICODE_CASE, UNICODE_CHARACTER_CLASS и UNIX_LINESสามารถรวมกันได้โดยใช้ระดับบิต OR (เช่นCASE_INSENSITIVE | DOTALL) และส่งผ่านเป็นอาร์กิวเมนต์flags

  • ด้วยข้อยกเว้นของCANON_EQ, LITERAL и UNICODE_CHARACTER_CLASSค่าคงที่เหล่านี้เป็นทางเลือกแทนนิพจน์แฟล็กแบบซ้อนที่แสดงในส่วนที่ 1 ถ้าพบค่าคงที่แฟล็กอื่นนอกเหนือจากที่กำหนดไว้ในคลาสPatternวิธีการPattern compile(String regex, int flags) จะส่งข้อjava.lang.IllegalArgumentExceptionยกเว้น ตัวอย่างPattern p = Pattern.compile("^\\.", Pattern.MULTILINE);เช่น เทียบเท่ากับตัวอย่างก่อนหน้า โดยมีค่าคงที่Pattern.MULTILINEและนิพจน์แฟล็กที่ซ้อน(?m)กันทำสิ่งเดียวกัน
บางครั้งจำเป็นต้องได้รับสำเนาของสตริงดั้งเดิมของนิพจน์ทั่วไปที่คอมไพล์เป็นอ็อบเจ็กต์Patternพร้อมกับแฟล็กที่ใช้ เมื่อต้องการทำเช่นนี้ คุณสามารถเรียกวิธีการต่อไปนี้:
  • String pattern()ส่งคืนสตริงนิพจน์ทั่วไปดั้งเดิมที่คอมไพล์เป็นไฟล์Pattern.

  • int flags()Patternส่งคืนแฟล็ กของวัตถุ
หลังจากได้รับวัตถุแล้วPatternโดยทั่วไปจะใช้เพื่อรับวัตถุMatcherเพื่อดำเนินการจับคู่รูปแบบ วิธีการMatcher matcher(Charsequence input)สร้างวัตถุMatcherที่ค้นหาข้อความinputเพื่อให้ตรงกับรูปแบบของPatternวัตถุ เมื่อเรียกก็จะส่งกลับการอ้างอิงไปยังวัตถุMatcherนี้ ตัวอย่างเช่น คำสั่งส่ง Matcher m = p.matcher(args[1]);คืนMatcherอ็อบเจ็กต์Patternที่อ้างอิงโดยตัวแปรp
การค้นหาเพียงครั้งเดียว
วิธีstatic boolean matches(String regex, CharSequence input)การเรียนPatternช่วยให้คุณประหยัดในการสร้างวัตถุPatternและMatcherการค้นหาครั้งเดียวโดยใช้เทมเพลต เมธอดนี้จะคืนค่าเป็นจริงหากinputรูปแบบตรงกันregexมิฉะนั้นจะส่งคืนค่าเท็จ หากนิพจน์ทั่วไปมีข้อผิดพลาดทางไวยากรณ์ เมธอดจะแสดงข้อPatternSyntaxExceptionยกเว้น ตัวอย่างเช่นSystem.out.println(Pattern.matches("[a-z[\\s]]*", "all lowercase letters and whitespace only"));พิมพ์trueเพื่อยืนยันว่าวลีall lowercase letters and whitespace onlyมีเพียงช่องว่างและอักขระตัวพิมพ์เล็ก
นิพจน์ทั่วไปใน Java ตอนที่ 3 - 2

การแยกข้อความ

นักพัฒนาส่วนใหญ่มีโค้ดที่เขียนอย่างน้อยหนึ่งครั้งเพื่อแบ่งข้อความที่ป้อนออกเป็นส่วนต่างๆ เช่น การแปลงบัญชีพนักงานแบบข้อความเป็นชุดช่อง คลาสนี้Patternช่วยให้แก้ปัญหางานที่น่าเบื่อนี้ได้สะดวกยิ่งขึ้นโดยใช้วิธีแยกข้อความสองวิธี:
  • วิธีการString[] split(CharSequence text, int limit)แยกtextตามที่พบตรงกับรูปแบบวัตถุPatternและส่งกลับผลลัพธ์ในอาร์เรย์ แต่ละองค์ประกอบอาร์เรย์ระบุลำดับข้อความที่แยกจากลำดับถัดไปโดยส่วนข้อความที่ตรงกับรูปแบบ (หรือส่วนท้ายของข้อความ) องค์ประกอบของอาร์เรย์อยู่ในลำดับเดียวกับที่ปรากฏtextใน

    ในวิธีนี้ จำนวนองค์ประกอบอาร์เรย์จะขึ้นอยู่กับพารามิเตอร์limitซึ่งควบคุมจำนวนรายการที่ตรงกันที่จะพบด้วย

    • ค่าบวกจะค้นหาไม่เกินค่าlimit-1ที่ตรงกัน และความยาวของอาร์เรย์ไม่เกินlimitองค์ประกอบ
    • หากค่าเป็นลบ ระบบจะค้นหารายการที่ตรงกันที่เป็นไปได้ทั้งหมด และความยาวของอาร์เรย์สามารถกำหนดเองได้
    • หากค่าเป็นศูนย์ ระบบจะค้นหารายการที่ตรงกันที่เป็นไปได้ทั้งหมด ความยาวของอาเรย์สามารถกำหนดเองได้ และบรรทัดว่างที่ส่วนท้ายจะถูกละทิ้ง

  • วิธีการString[] split(CharSequence text)เรียกวิธีการก่อนหน้านี้ด้วย 0 เป็นอาร์กิวเมนต์จำกัดและส่งกลับผลลัพธ์ของการเรียก
ด้านล่างนี้เป็นผลลัพธ์ของวิธีsplit(CharSequence text)การแก้ไขปัญหาการแบ่งบัญชีพนักงานออกเป็นช่องชื่อ อายุ ที่อยู่ทางไปรษณีย์ และเงินเดือน:
Pattern p = Pattern.compile(",\\s");
String[] fields = p.split("John Doe, 47, Hillsboro Road, 32000");
for (int i = 0; i < fields.length; i++)
   System.out.println(fields[i]);
โค้ดด้านบนอธิบายนิพจน์ทั่วไปเพื่อค้นหาอักขระลูกน้ำตามด้วยอักขระเว้นวรรคตัวเดียวทันที นี่คือผลลัพธ์ของการดำเนินการ:
John Doe
47
Hillsboro Road
32000

เพรดิเคตเทมเพลตและ Streams API

ใน Java 8 Patternวิธีการปรากฏ ในคลาส . วิธีการนี้จะสร้างเพรดิเคต (ฟังก์ชันที่มีค่าบูลีน) ที่ใช้เพื่อให้ตรงกับรูปแบบ การใช้วิธีนี้จะแสดงอยู่ในข้อมูลโค้ดต่อไปนี้: Predicate asPredicate()
List progLangs = Arrays.asList("apl", "basic", "c", "c++", "c#", "cobol", "java", "javascript", "perl", "python", "scala");
Pattern p = Pattern.compile("^c");
progLangs.stream().filter(p.asPredicate()).forEach(System.out::println);
โค้ดนี้จะสร้างรายการชื่อภาษาการเขียนโปรแกรม จากนั้นรวบรวมรูปแบบเพื่อค้นหาชื่อทั้งหมดที่ขึ้นต้นด้วยตัวcอักษร บรรทัดสุดท้ายของโค้ดด้านบนใช้รับสตรีมข้อมูลแบบอนุกรมโดยมีรายการนี้เป็นแหล่งที่มา โดยจะตั้งค่าตัวกรองโดยใช้ฟังก์ชันบูลีนasPredicate()ที่คืนค่าเป็นจริงเมื่อชื่อขึ้นต้นด้วยตัวอักษรcและวนซ้ำผ่านสตรีม โดยพิมพ์ชื่อที่ตรงกันไปยังเอาต์พุตมาตรฐาน บรรทัดสุดท้ายนี้เทียบเท่ากับการวนซ้ำปกติต่อไปนี้ ซึ่งคุ้นเคยจากแอปพลิเคชัน RegexDemo จากส่วนที่ 1:
for (String progLang: progLangs)
   if (p.matcher(progLang).find())
      System.out.println(progLang);

วิธีการคลาส Matcher

อินสแตนซ์ของคลาสMatcherอธิบายกลไกในการดำเนินการจับคู่รูปแบบตามลำดับอักขระโดยการตีความนิพจน์ทั่วไปที่คอมไพล์แล้วของPatternคลาส อ็อบเจ็กต์ของคลาสMatcherสนับสนุนการดำเนินการค้นหารูปแบบประเภทต่างๆ:
  • วิธีการboolean find()ค้นหาข้อความที่ป้อนสำหรับการจับคู่ครั้งต่อไป วิธีการนี้จะเริ่มการสแกนที่จุดเริ่มต้นของข้อความที่ระบุหรือที่อักขระตัวแรกหลังจากการจับคู่ครั้งก่อน ตัวเลือกที่สองจะเป็นไปได้ก็ต่อเมื่อการเรียกเมธอดนี้ก่อนหน้านี้คืนค่าเป็น "จริง" และไม่ได้รีเซ็ตรีโซลเวอร์ ไม่ว่าในกรณีใด หากการค้นหาสำเร็จ ค่าบูลีนที่เป็นจริงจะถูกส่งกลับ ตัวอย่างของวิธีนี้สามารถพบได้ในRegexDemoส่วนที่ 1

  • วิธีการนี้boolean find(int start)จะรีเซ็ตตัวจับคู่และค้นหาข้อความสำหรับการจับคู่ครั้งต่อไป การดูเริ่มต้นจากตำแหน่งที่ระบุโดยstartพารามิเตอร์ หากการค้นหาสำเร็จ ค่าบูลีนจริงจะถูกส่งกลับ ตัวอย่างเช่นm.find(1);สแกนข้อความโดยเริ่มจากตำแหน่ง1(ตำแหน่ง 0 จะถูกละเว้น) หากพารามิเตอร์startมีค่าลบหรือค่าที่มากกว่าความยาวของข้อความที่ตรงกัน วิธีการจะส่งข้อjava.lang.IndexOutOfBoundsExceptionยกเว้น

  • วิธีการนี้boolean matches()จะพยายามจับคู่ข้อความทั้งหมดกับรูปแบบ มันจะส่งกลับค่าบูลีนจริงหากข้อความทั้งหมดตรงกับรูปแบบ ตัวอย่างเช่น โค้ดPattern p = Pattern.compile("\\w*"); Matcher m = p.matcher("abc!"); System.out.println(p.matches());แสดงผลfalseเนื่องจากอักขระ!ไม่ใช่อักขระคำ

  • วิธีการนี้boolean lookingAt()จะพยายามจับคู่ข้อความที่กำหนดกับรูปแบบ เมธอดนี้จะคืนค่าเป็นจริงหากส่วนใดส่วนหนึ่งของข้อความตรงกับรูปแบบ ต่างจาก method นี้ตรงที่matches();ข้อความทั้งหมดไม่จำเป็นต้องตรงกับรูปแบบ ตัวอย่างเช่นPattern p = Pattern.compile("\\w*"); Matcher m = p.matcher("abc!"); System.out.println(p.lookingAt());จะส่งออกtrueเนื่องจากจุดเริ่มต้นของข้อความabc!ประกอบด้วยอักขระที่สร้างคำเท่านั้น

วัตถุคลาสจะเก็บข้อมูลสถานะ ต่างจาก Patternคลาสอ็อบเจ็กต์ Matcherบางครั้งคุณอาจต้องรีเซ็ตตัวจับคู่เพื่อล้างข้อมูลนี้หลังจากการค้นหารูปแบบเสร็จสิ้น วิธีการต่อไปนี้มีไว้เพื่อรีเซ็ตตัวแก้ไข:
  • วิธีการนี้Matcher reset()จะรีเซ็ตสถานะของตัวจับคู่ รวมถึงตำแหน่งที่จะต่อท้าย (รีเซ็ตเป็น 0) การดำเนินการค้นหารูปแบบถัดไปเริ่มต้นที่จุดเริ่มต้นของข้อความที่ตรงกัน การอ้างอิงถึงวัตถุปัจจุบันจะถูกส่งMatcherกลับ ตัวอย่างเช่นm.reset();รีเซ็ตตัวแก้ไขที่อ้างอิงmโดย

  • วิธีการMatcher reset(CharSequence text)รีเซ็ตสถานะตัวแก้ไขและตั้งค่าข้อความตัวแก้ไขใหม่textเป็น การดำเนินการค้นหารูปแบบถัดไปเริ่มต้นที่จุดเริ่มต้นของข้อความตัวจับคู่ใหม่ การอ้างอิงถึงวัตถุปัจจุบันจะถูกส่งMatcherกลับ ตัวอย่างเช่นm.reset("new text");รีเซ็ตรีโซลเวอร์ที่อ้างอิงmและตั้งค่าข้อความรีโซลเวอร์ใหม่"new text"เป็น

นิพจน์ทั่วไปใน Java ตอนที่ 3 - 3

การเพิ่มข้อความต่อท้าย

java.lang.StringBufferตำแหน่งของตัวจับคู่ที่จะต่อท้ายระบุจุดเริ่มต้นของข้อความตัวจับคู่ที่ต่อท้ายวัตถุประเภท วิธีการต่อไปนี้ใช้ตำแหน่งนี้:
  • วิธีการ อ่านอักขระ ข้อความMatcher appendReplacement(StringBuffer sb, String replacement)ที่ตรงกันและต่อท้ายวัตถุStringBufferที่อ้างอิงโดยอาร์กิวเมนต์ sbวิธีการนี้จะหยุดอ่านที่อักขระตัวสุดท้ายที่อยู่ก่อนหน้าการจับคู่รูปแบบก่อนหน้า ถัดไป วิธีการต่อท้ายอักขระจากออบเจ็กต์ประเภทStringที่อ้างอิงโดยอาร์กิวเมนต์replacementต่อท้ายออบเจ็กต์StringBuffer(สตริงreplacementอาจมีการอ้างอิงถึงลำดับข้อความที่บันทึกไว้ระหว่างการค้นหาครั้งก่อน โดยระบุโดยใช้อักขระ($)และหมายเลขกลุ่มที่ถูกจับ) สุดท้ายนี้ เมธอดจะตั้งค่าของตำแหน่งตัวจับคู่เพื่อต่อท้ายตำแหน่งของอักขระที่ตรงกันตัวสุดท้ายบวกหนึ่ง แล้วส่งคืนการอ้างอิงไปยังตัวจับคู่ปัจจุบัน

  • วิธีการนี้Matcher appendReplacement(StringBuffer sb, String replacement)จะส่งข้อยกเว้นjava.lang.IllegalStateExceptionหากผู้จับคู่ยังไม่พบรายการที่ตรงกันหรือความพยายามในการค้นหาครั้งก่อนล้มเหลว มันจะส่งข้อยกเว้นIndexOutOfBoundsExceptionหากบรรทัดreplacementระบุกลุ่มการจับที่ไม่อยู่ในรูปแบบ)

  • วิธีการStringBuffer appendTail(StringBuffer sb)เพิ่มข้อความทั้งหมดให้กับวัตถุStringBufferและส่งกลับการอ้างอิงไปยังวัตถุนั้น หลังจากการเรียกเมธอดครั้งล่าสุดappendReplacement(StringBuffer sb, String replacement)ให้เรียกเมธอดappendTail(StringBuffer sb)เพื่อคัดลอกข้อความที่เหลือไปยังออบเจ็กStringBufferต์

กลุ่มที่ถูกจับ
ดังที่คุณจำได้จากส่วนที่ 1 กลุ่มแคปเจอร์คือลำดับของอักขระที่อยู่ในวงเล็บ ( ()) เมตาอักขระ วัตถุประสงค์ของโครงสร้างนี้คือเพื่อจัดเก็บอักขระที่พบเพื่อนำมาใช้ใหม่ในภายหลังในระหว่างการจับคู่รูปแบบ ตัวละครทั้งหมดจากกลุ่มที่ถูกจับจะถือเป็นตัวละครตัวเดียวในระหว่างการค้นหารูปแบบ
รหัสต่อไปนี้เรียก เมธอด appendReplacement(StringBuffer sb, String replacement)และappendTail(StringBuffer sbเพื่อแทนที่ลำดับอักขระทั้งหมดในข้อความต้นฉบับcatด้วยcaterpillar:
Pattern p = Pattern.compile("(cat)");
Matcher m = p.matcher("one cat, two cats, or three cats on a fence");
StringBuffer sb = new StringBuffer();
while (m.find())
   m.appendReplacement(sb, "$1erpillar");
m.appendTail(sb);
System.out.println(sb);
การใช้กลุ่มที่บันทึกและการอ้างอิงถึงกลุ่มนั้นในข้อความแทนที่จะบอกให้โปรแกรมแทรกerpillarหลังจากการเกิดขึ้นแต่ละครั้งของcat. ผลลัพธ์ของการรันโค้ดนี้มีลักษณะดังนี้: one caterpillar, two caterpillars, or three caterpillars on a fence

การแทนที่ข้อความ

คลาสนี้Matcherมีสองวิธีในการแทนที่ข้อความ ซึ่งเสริมกับappendReplacement(StringBuffer sb, String replacement). เมื่อใช้วิธีการเหล่านี้ คุณสามารถแทนที่รายการแรกของ [ข้อความที่ถูกแทนที่] หรือรายการทั้งหมดที่เกิดขึ้นได้:
  • วิธีการString replaceFirst(String replacement)รีเซ็ตตัวจับคู่ สร้างวัตถุใหม่Stringคัดลอกอักขระทั้งหมดของข้อความตัวจับคู่ (จนถึงคู่แรก) ไปยังสตริงนี้ เพิ่มอักขระต่อท้ายจากจุดสิ้นสุดreplacementคัดลอกอักขระที่เหลือไปยังสตริง และส่งกลับ วัตถุString(สตริงreplacementสามารถมีการอ้างอิงถึงสิ่งที่บันทึกไว้ระหว่างลำดับข้อความค้นหาก่อนหน้าโดยใช้สัญลักษณ์ดอลลาร์และหมายเลขกลุ่มที่บันทึก)

  • วิธีString replaceAll(String replacement)การทำงานคล้ายกับ method String replaceFirst(String replacement)แต่แทนที่replacementรายการที่ตรงกันทั้งหมดด้วยอักขระจากสตริง

นิพจน์ทั่วไป\s+ค้นหาอักขระช่องว่างตั้งแต่หนึ่งตัวขึ้นไปในข้อความอินพุต ด้านล่างนี้เราจะใช้นิพจน์ทั่วไปนี้และเรียกวิธีreplaceAll(String replacement)การลบช่องว่างที่ซ้ำกัน:
Pattern p = Pattern.compile("\\s+");
Matcher m = p.matcher("Удаляем      \t\t лишние пробелы.   ");
System.out.println(m.replaceAll(" "));
นี่คือผลลัพธ์: Удаляем лишние пробелы. นิพจน์ทั่วไปใน Java ตอนที่ 4 นิพจน์ทั่วไปใน Java ส่วนที่ 5
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION