บทความทั้งหมด

Regular Expression: พื้นฐานและกับดักของการจับคู่แบบโลภ

นิพจน์ปกติ (regex) เป็นวิธีอธิบายรูปแบบข้อความอย่างกระชับ — ไม่ใช่สตริงที่เจาะจง แต่เป็นกฎที่สตริงต้องสอดคล้องด้วย แทนที่จะวนลูปตรวจทีละตัวอักษร สามารถใช้นิพจน์เดียวตรวจสอบหรือค้นหาโครงสร้างที่ซับซ้อนได้

ส่วนประกอบพื้นฐาน

  • คลาสอักขระ\d (ตัวเลข), \w (ตัวอักษร/ตัวเลข/ขีดล่าง), \s (ช่องว่าง), หรือคลาสกำหนดเองอย่าง [a-zA-Z]
  • ตัวระบุจำนวน* (0 ครั้งขึ้นไป), + (1 ครั้งขึ้นไป), ? (0 หรือ 1 ครั้ง), {2,5} (2 ถึง 5 ครั้ง)
  • กลุ่มจับคู่ (capture group) — วงเล็บ (...) ทำเครื่องหมายส่วนหนึ่งของผลลัพธ์ที่ตรงกัน เพื่อนำไปใช้แยกต่างหากได้ เช่นตอนแทนที่ข้อความ
  • สมอ (anchor)^ และ $ ยึดรูปแบบไว้กับจุดเริ่มต้นหรือจุดสิ้นสุดของบรรทัด

ทำไม \w จึงมองไม่เห็นภาษาไทย

ในเอนจิน regex ของ JavaScript ที่เครื่องมือนี้ใช้ \w หมายถึง [A-Za-z0-9_] เท่านั้นเสมอ — ตัวอักษรละติน ตัวเลข และขีดล่าง ไม่รวมอักษรไทยแม้แต่ตัวเดียว ดังนั้น /^\w+$/.test('สวัสดี') จะคืนค่า false ทันที การเพิ่มแฟล็ก u ก็ไม่ได้ขยายความหมายของ \w แต่อย่างใด หากต้องการจับข้อความภาษาไทยให้ถูกต้อง ต้องใช้คลาสที่อิงคุณสมบัติ Unicode โดยตรง เช่น /^[\p{L}\p{M}]+$/u โดย \p{L} ครอบคลุมพยัญชนะและสระ ส่วน \p{M} จำเป็นสำหรับวรรณยุกต์และสระที่เป็นเครื่องหมายประกอบ (combining mark) ซึ่งแยกจากตัวอักษรฐานในระดับโค้ดพอยต์

การจับคู่แบบโลภกับแบบขี้เกียจ

โดยค่าเริ่มต้น ตัวระบุจำนวนจะ "โลภ" — พยายามจับตัวอักษรให้ได้มากที่สุด และยอมถอยก็ต่อเมื่อสิ่งนั้นขัดขวางการจับคู่ ตัวอย่างเช่น <.+> บนสตริง <a>text</a> จะจับทุกอย่างตั้งแต่ < ตัวแรกไปจนถึง > ตัวสุดท้าย การเติม ? ต่อท้ายตัวระบุจำนวน (<.+?>) จะทำให้มันกลายเป็น "ขี้เกียจ" — จับให้น้อยที่สุดเท่าที่จะเป็นไปได้ และหยุดที่การจับคู่ที่ถูกต้องแรกสุด

ใช้ทำอะไรได้บ้าง

  • ตรวจสอบรูปแบบข้อมูลนำเข้า (อีเมล เบอร์โทรศัพท์ รหัสไปรษณีย์)
  • ค้นหาและแทนที่ข้อความจำนวนมากตามรูปแบบ แทนการจับคู่แบบตรงเป๊ะ
  • ดึงข้อมูลที่มีโครงสร้างออกจากล็อกหรือข้อความที่ไม่มีโครงสร้าง

Backtracking แบบหายนะ

ตัวระบุจำนวนที่ซ้อนกันอย่าง (a+)+ อาจบังคับให้เอนจิน regex ลองชุดค่าผสมจำนวนมหาศาลแบบเอ็กซ์โพเนนเชียลก่อนจะสรุปว่าไม่มีการจับคู่ในบางอินพุต — หน้าเว็บหรือเซิร์ฟเวอร์ "ค้าง" กับนิพจน์ที่ดูเรียบง่าย นี่คือช่องโหว่จริง (ReDoS) ดังนั้นตัวระบุจำนวนซ้อนกันที่ซับซ้อนควรทดสอบกับสตริงยาวที่ "เกือบตรงกัน" ด้วย ไม่ใช่แค่ตัวอย่างที่คาดหวังไว้เท่านั้น

ลองใช้เครื่องมือ