Regular Expression หรือเรียกย่อๆว่า “ regex ” เป็นวิธีการที่เราใช้อธิบายรูปแบบของข้อความ (String) เรามักจะใช้ regex ในการค้นหารูปแบบอย่างเฉพาะเจาะจงจากข้อความ จากการเรียนรู้ที่ผ่านมาเราได้ใช้งาน regex โดยไม่รู้ตัวเพราะเมธอดของคลาส String หลายๆเมธอดให้เราใช้ regex เป็นพารามิเตอร์
รูปแบบของ regex ก็คือข้อความ ดังนั้นรูปแบบที่ง่ายที่สุดของ regex ก็คือข้อความ เช่น “Hello World” ก็เป็น regex
ตัวอย่างการใช้ regex อย่างง่ายๆ เช่น หากเราต้องการแทนที่คำว่า Hello ในข้อความ “Hello World” ด้วยคำว่า Hey! เราใช้เมธอด replaceAll ของคลาส String คือ replaceAll(“Hello”, “Hey!”) ซึ่งพารามิเตอร์ตัวแรกคือ regex เพื่อบอกรูปแบบของข้อความที่ต้องการค้นหา ส่วนพารามิเตอร์ตัวที่สองคือข้อความที่ต้องการนำไปแทนที่ และเมื่อเราไปดูคู่มือเมธอดก็จะพบการระบุว่าเมธอด replaceAll(String regex, String replacement) ซึ่งพารามิเตอร์แรกคือการรับ Regular Expression ด้วยชนิดข้อมูล String
regex มีความสามารถที่ช่วยให้เรากำหนดรูปแบบได้ง่ายขึ้นและสะดวกขึ้น เช่น การใช้ character class ซึ่งเหมือนกับการใช้ wild card เช่นการใช้ . * ? + \d \D \s \S \w \W ตัวอย่างเช่น . หมายถึง อักขระใดๆ ถ้าเราใช้ replaceAll(“.”, O) กับ “Hello World” จะได้ “OOOOOOOOOOO” หรือการใช้ boundary matchers ซึ่งเป็นการระบุขอบเขตที่ต้องการ เช่น จุดเริ่มต้นหรือจุดสิ้นสุดของคำหรือข้อความ เช่นการใช้ ^ $ \b \B \A \G \Z \z ตัวอย่างเช่น ถ้าเราใช้ replaceAll(“^H”,”W”) กับ “Hello Hello” จะได้ “Wello Hello” เพราะ ^ หมายถึงต้นข้อความเท่านั้น และถ้าเราใช้ replaceAll(“lo$”,”AA”) จะหมายถึง lo ที่ท้ายข้อความเท่านั้น
ถ้าเราต้องการแทนที่ตัวอักษรบางตัวหรือหลายตัวด้วยตัวอักษรที่กำหนด เราจะใช้อักขระ [ ] เช่น ถ้าเราใช้ replaceAll(“[el]”,”P”) กับ “Hello World” จะได้ “HPPPo WorPd”
การแทนที่ตัวอักษรไม่จำเป็นต้องเป็นหนึ่งต่อหนึ่งเสมอไป เช่น ถ้าเราใช้ replaceAll(“[e]”,”english word”) กับ “Hello World” จะได้ “Henglish wordllo Word”
และถ้าเราต้องการแทนที่เฉพาะ l ที่ตามด้วย o หรือ d เราใช้อักขระ [ ] 2 ชุดต่อกัน เช่น replaceAll(“[l][od]”,”P”)
ถ้าเราต้องการค้นหาชื่อที่อาจจะขึ้นต้นด้วยตัวอักษรใหญ่หรือเล็กก็ได้เราใช้ [Ss]ombat เช่น replaceAll(“[Ss]ombat”,Somchai”)
ถ้าเราต้องการแทนที่ทุกตัวอักษรด้วย P ยกเว้นอักษร e และ d ใน “Hello World” เราใช้ replaceAll(“[^ed]”,”P”) ซึ่งจะได้ “PePPP PPPPd” และในที่นี้ ^ มีบทบาทเป็น character class ไม่ใช่ boundary matchers ดังตัวอย่างก่อนหน้าเพราะเราเขียนไว้ภายใต้ [ ]
ถ้าเราต้องการแทนที่อักษร abcdef และ ABCDEF และตัวเลข 12345 เราสามารถเขียนได้สั้นๆเป็น”[a-fA-F1-5]” หรือ “(?i)[a-f1-5]” ในกรณีที่เป็นรหัส ascii หรือ “(?iu)[a-f1-5]” ในกรณีที่เป็นรหัส unicode
ถ้าเราต้องการแทนที่เฉพาะตัวเลขทั้งหมดในข้อความเราใช้ replaceAll(“\\d”, “X”) และในทางกลับกัน ถ้าเราต้องการแทนที่เฉพาะตัวอักษรทั้งหมดในข้อความเราใช้ replaceAll(“\\D”, “X”)
ถ้าต้องการระบุถึง space tab หรือ new line เราใช้ \\s ถ้าต้องการระบุถึงตัวอักษรและตัวเลขทั้งหมดเราใช้ \\w
ถ้าเราต้องการห่อหุ้มทุกคำด้วยอักขระ # เราใช้ replaceAll(“\\b”, “#”) เช่นถ้าใช้กับ “Hello World” จะได้ “#Hello# # World#”
เราสามารถระบุจำนวนตัวอักษรได้ด้วยการใช้ตัว quantifiers เช่น ถ้าเราต้องการระบุ abccc เราเขียนได้เป็น “abc{3}” หรือถ้าเราต้องการระบุว่า ab ตามด้วย c กี่ตัวก็ได้ เราเขียนได้เป็น “abc+” หรือถ้าเราต้องการระบุว่า ab ตามด้วย c กี่ตัวก็ได้ หรือ ไม่มี c ตามมาก็ได้ เราเขียนได้เป็น “abc*” ถ้าเราต้องการระบุว่า ab ตามด้วย c 3ตัวถึง7ตัวเท่านั้น เราเขียนได้เป็น “abc{3,7}” สังเกตุว่าตัว quantifiers จะมีผลกับตัวอักษรก่อนหน้า ดังนั้นเราสามารถใช้ร่วมกันเพื่อระบุสิ่งที่ซับซ้อนๆได้ เช่น “a+c{3}h” หมายถึงข้อความที่มี a ข้างหน้ากี่ตัวก็ได้ตามด้วย c 3 ตัวและตามด้วย h
สำหรับการใช้รูปแบบในแบบต่างๆ สามารถดูได้จาก https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html
นอกจากการใช้ regex โดยตรงแล้ว เราสามารถคอมไพล์ regex เป็นออบ
เจกต์ Pattern โดยใช้เมธอด compile() ของคลาส Pattern
Pattern p = Pattern.compile(“a*b”);
ซึ่งออบเจกต์ Pattern จะถูกนำไปใช้ในการสร้างออบเจกต์ Matcher ซึ่งเกิดจากการเปรียบเทียบ Pattern กับข้อความที่กำหนดโดยใช้เมธอด matcher() ของคลาส Pattern
Matcher m = p.matcher(“aaaaab”);
ผลลัพธ์ที่ได้จากการเปรียบเทียบจะเก็บไว้ในออบเจกต์ Matcher เพื่อนำไปใช้งานต่อไป เช่น ดูผลลัพธ์ว่าพบข้อความตามรูปแบบหรือไม่ด้วยมเธอด matches() ของคลาส Matcher
boolean b = m.matches();
สิ่งที่ต้องเข้าใจในการใช้ออบเจกต์ Matcher ก็คือจะมีการเก็บสถานะว่ามีการใช้งานแล้วเมื่อเราเรียกใช้เมธอดของออบเจกต์ Matcher ดังนั้นหากเราต้องการใช้ออบเจกต์ Matcher ตัวเดิมอีกครั้งเราจะต้องเคลียร์สถานะดังกล่าวด้วยเมธอด reset()
การใช้ออบเจกต์ Matcher ช่วยให้เราเข้าถึงข้อมูลที่ได้จากการเปรียบเทียบข้อความ เช่น แสดงตำแหน่งของข้อความที่ต้องการด้วยเมธอด start() และเมธอด end() โดยเราใช้วิธีวนรอบเพื่อตรวจสอบผลลัพธ์จากเมธอด find() ซึ่งจะให้ผลลัพธ์เป็น true หรือ false ขึ้นอยู่กับว่ายังมีข้อความตามรูปแบบที่กำหนดอีกหรือไม่นับจากการพบในรอบก่อน
จากตัวอย่างด้านล่างบรรทัดที่ 6 เป็นการเตรียมข้อความที่ต้องการค้นหา บรรทัดที่ 19 เป็นการสร้างออบเจกต์ Pattern ชื่อ p ซึ่งบรรจุรูปแบบ regex “Java” เอาไว้ บรรทัดที่ 20 เป็นการสร้างออบเจกต์ Matcher ขึ้นมาโดยระบุโดยใช้ข้อความใน stb เป็นพารามิเตอร์ บรรทัดที่ 22 สร้างลูปโดยตรวจสอบค่าที่ได้จากเมธอด find() บรรทัดที่ 24 สั่งพิมพ์ตำแหน่ง (ดัชนี) ของข้อความที่พบ
เราสามารถดึงข้อความที่ต้องการออกมาด้วยการใช้เมธอด group()โดยการครอบ regex ด้วย ( ) ซึ่งผลลัพธ์ที่ได้คือกลุ่มของข้อความซึ่งมีดัชนีประจำกลุ่มตั้งแต่ 1 เป็นต้นไปตามจำนวน ( ) ที่เรากำหนดใน regex (ดัชนี 0 คือข้อความที่ตรงตามรูปแบบที่ต้องการเปรียบเทียบ)
จากตัวอย่างด้านล่าง บรรทัดที่ 6 เป็นข้อมูลตัวอย่างในรูปแบบ xml บรรทัดที่ 25 เป็นการสร้างออบเจกต์ Pattern สังเกตว่าเราครอบ regex ด้วย ( ) เพื่อใช้ความสามารถของเมธอด group ซึ่งในที่นี้จะได้กลุ่มที่มีดัชนี 1 – 3 บรรทัดที่ 26 เป็นการสร้างออบเจกต์ Matcher และบรรทัดที่ 27 – 31 เป็นการวนรอบเพื่อพิมพ์ข้อมูลจากเมธอด group() ดังนั้นจากตัวอย่าง ถ้าเราต้องการสกัดเอาเฉพาะชื่ออัลบั้ม เราจะใช้ข้อมูลจาก group(2)
เราสามารถใช้ตรรกะ and, or และ not กับ regex ได้ โดยที่ผ่านมาเราใช้ตรรกะ and โดยไม่รู้ตัว เช่น “hello” อันที่จริงคือ h and e and l and l and o นั่นเอง
สำหรับตรรกะ or เราใช้อักขระ | เพื่อบอกว่า “ทั้งสอง” โดยครอบเงื่อนไขด้วย [ ] เช่น “[h|H]ello” หมายถึง hello และ Hello
ส่วนการใช้ not เป็นการใช้อักขระ ^ กำกับเพื่อบอกว่า “ไม่ใช่” โดยครอบเงื่อนไขด้วย [ ] เช่น “[^abc]” หมายถึงไม่ใช่ abc และถ้าเราต้องการค้นหาตำแหน่งของตัว h ที่ไม่ได้ตามด้วย a จากข้อความ “haheh” เราจะใช้ “[h^a]” ซึ่งจะได้ตำแหน่งของ h ที่ตามด้วย a แต่ถ้าเราต้องการให้รวม h ตัวสุดท้ายด้วย เราจะใช้ “h(?!a)”และถ้าเราต้องการค้นหาตำแหน่งของตัว h ที่ตามด้วย a รวมถึง h ตัวสุดท้ายด้วย เราจะใช้ “h(?=a)”