นากกายวางไม้บรรทัดครบความยาวสติกเกอร์ ไม่เหลือขอบเกิน

Python re.fullmatch: ตรวจทั้งสตริง ไม่ให้ส่วนเกินหลุดผ่าน

น้องกายEngineering

ถ้าใช้ re.search ตรวจรหัสคูปอง แพทเทิร์นอาจไปตรงกลางสตริงแล้วปล่อยให้ขยะนำหน้า/ตามท้ายผ่านได้ re.fullmatch บังคับให้ทั้งสตริงตรงแพทเทิร์นครับ เหมาะกับค่าที่ต้องเป๊ะทั้งก้อน

WHY: ขอบเขตทั้งค่า ไม่ใช่แค่เจอชิ้นส่วน

เหมือนตรวจบัตรคิวที่ต้องตรงเลขทั้งใบ ไม่ใช่แค่เห็นเลขบางหลักในมุม การ validate ชื่อไฟล์ รหัสเชิญ หรือโทเคนสั้น ๆ ถ้าใช้ search อาจรับ xxxTOKENyyy ทั้งที่ต้องการแค่ TOKEN

import re
import unittest

COUPON = re.compile(r"[A-Z0-9]{4}-[A-Z0-9]{4}")


def valid_coupon(raw: str) -> bool:
    return COUPON.fullmatch(raw.strip()) is not None


class FullmatchTests(unittest.TestCase):
    def test_accepts_exact(self) -> None:
        self.assertTrue(valid_coupon("AB12-CD34"))

    def test_rejects_prefix_suffix(self) -> None:
        self.assertFalse(valid_coupon("xxAB12-CD34"))
        self.assertFalse(valid_coupon("AB12-CD34yy"))

    def test_search_would_lie(self) -> None:
        self.assertIsNotNone(COUPON.search("xxAB12-CD34yy"))
        self.assertFalse(valid_coupon("xxAB12-CD34yy"))


if __name__ == "__main__":
    unittest.main()

HOW: fullmatch แทนการหุ้ม ^…$ เอง

fullmatch(pattern, string) เทียบเท่าการค้นทั้งสตริงตั้งแต่ต้นจนจบ โค้ดอ่านง่ายกว่าการเขียน ^ กับ $ เองทุกครั้ง และลดโอกาสลืมจุดยึดปลายด้านใดด้านหนึ่ง

ถ้าต้องการไม่สนตัวพิมพ์ ใช้ re.IGNORECASE ตอน compile แล้วทดสอบทั้งเคสผ่านกับเคสมีช่องว่างเกิน

GOTCHA: strip ก่อนหรือไม่

fullmatch มองช่องว่างเป็นตัวอักษร ถ้าอินพุตจากฟอร์มมีช่องว่างข้าง ๆ ให้ strip() ก่อน หรือใส่ช่องว่างในแพทเทิร์นอย่างจงใจ

Recap

  • ใช้ fullmatch เมื่อต้องตรงทั้งค่า
  • อย่าพึ่ง search สำหรับ validation แบบเป๊ะ
  • ตัดสินใจเรื่อง strip / flags ให้สอดคล้องกับสัญญาอินพุต