2.36. กลุ่มและจุดยึด¶
รูปแบบสามารถทำได้มากกว่าแค่บอกว่า "สตริงนี้ตรงกัน" -- มันสามารถแยกชิ้นส่วนที่ตรงกันออกและส่งแต่ละส่วนให้แอปพลิเคชันตามชื่อ วงเล็บรอบส่วนหนึ่งของรูปแบบจะทำให้กลายเป็น กลุ่มจับค่า (capturing group) และออบเจ็กต์การจับคู่จะแสดงแต่ละกลุ่มเป็นสตริงย่อยแยกต่างหาก
2.36.1. กลุ่มจับค่า (Capturing groups)¶
ใส่วงเล็บ (...) รอบส่วนใดก็ได้ของรูปแบบเพื่อจับสิ่งที่มันตรงกัน:
>>> import re
>>> m = re.search(r'temp (\d+) at (\d+)s', 'temp 42 at 137s ok')
>>> m.group(0)
'temp 42 at 137s'
>>> m.group(1)
'42'
>>> m.group(2)
'137'
กลุ่มที่ 0 คือการจับคู่ทั้งหมดเสมอ
กลุ่มที่ 1, 2, ... คือสตริงย่อยที่ถูกจับ โดยนับจากซ้ายไปขวาตาม วงเล็บเปิด ของแต่ละกลุ่ม
การเรียก
match.group()ด้วยดัชนีที่เกินกลุ่มสุดท้ายจะทำให้เกิดIndexError
รูปแบบทั่วไปคือ "จับคู่โครงสร้างที่รู้จัก แล้วจับส่วนที่แปรผันเป็น int":
def parse_temp(line):
m = re.search(r'temp (\d+) at (\d+)s', line)
if not m:
return None
return int(m.group(1)), int(m.group(2))
2.36.2. กลุ่มที่ไม่จับค่า (Non-capturing groups)¶
วงเล็บยังสามารถ จัดกลุ่ม นิพจน์ย่อยเพื่อให้ตัวกำหนดจำนวน (quantifier) ทำงานกับกลุ่มทั้งหมดได้ นั่นคือจุดประสงค์เดียวของการจัดกลุ่มใน r'(ab)+' -- "หนึ่งครั้งหรือมากกว่าของ ab" การที่ ab ปรากฏเป็นกลุ่มที่ 1 เป็นเพียงผลข้างเคียง
หากต้องการจัดกลุ่มโดยไม่จับค่า ให้ใช้ (?:...)
>>> re.search(r'(?:ab)+', 'xababy').group(0)
'abab'
กลุ่มที่ไม่จับค่าช่วยให้หมายเลขกลุ่มเป็นระเบียบ เมื่อรูปแบบใช้การจัดกลุ่มเพื่อโครงสร้างแต่ไม่ต้องการดึงแต่ละชิ้นออกมา
2.36.3. จุดยึด (Anchors)¶
จุดยึดไม่ได้จับคู่กับตัวอักษร -- แต่จับคู่กับ ตำแหน่ง
^-- จุดเริ่มต้นของสตริง$-- จุดสิ้นสุดของสตริง
จุดยึดคือสิ่งที่ทำให้ re.match() และ re.search() ทำงานต่างกัน re.match(p, s) เหมือนกับ re.search('^' + p, s) ตรงที่บังคับให้รูปแบบเริ่มต้นที่ตำแหน่ง 0 การเพิ่ม $ ท้ายรูปแบบจะทำให้รูปแบบจับคู่กับสตริง ทั้งหมด และไม่มีอะไรอื่น:
>>> re.search(r'^\d+$', '12345')
<match num=1>
>>> re.search(r'^\d+$', '12345 ok') is None
True
^ และ $ ใน MicroPython re หมายถึงจุดเริ่มต้นและจุดสิ้นสุดของสตริง ทั้งหมด ที่ส่งให้ re.search() เสมอ ไม่มีแฟล็ก re.MULTILINE เพื่อให้ทำงานกับทุกบรรทัดที่ฝังอยู่ และ $ ก็ไม่จับคู่กับตำแหน่งก่อน \n ท้าย -- ต้องเป็นจุดสิ้นสุดแบบสัมบูรณ์ของข้อมูล หากต้องการพฤติกรรมแบบรายบรรทัด ให้แยกข้อมูลที่บรรทัดใหม่ก่อน แล้วรันรูปแบบบนแต่ละบรรทัด
2.36.4. ชุดตัวอักษร (Character sets)¶
วงเล็บเหลี่ยมกำหนดชุดตัวอักษรที่ระบุไว้อย่างชัดเจน การจับคู่จะใช้ตัวอักษรหนึ่งตัวจากชุดนั้น
[abc]-- หนึ่งในบรรดาa,b,c[a-z]-- ตัวอักษรหนึ่งตัวในช่วงa-z(รวมปลายทั้งสอง)[a-zA-Z0-9]-- ตัวอักษรหรือตัวเลข รวมสามช่วง[^abc]-- ไม่ใช่ หนึ่งในบรรดาa,b,cโดย^จะกลับความหมายก็ต่อเมื่ออยู่ตำแหน่งแรกภายในวงเล็บเท่านั้น
ตัวอย่าง:
>>> re.search(r'[A-F0-9]{6}', 'colour #1a2b3c rest').group(0)
'1A2B3C'
>>> re.search(r'[A-F0-9]{6}', 'colour #1a2b3c rest') is None
True
การเรียกแรกคืนค่า None ในทางปฏิบัติเพราะข้อความที่ระบุเป็นตัวพิมพ์เล็ก MicroPython re ไม่มีแฟล็ก re.IGNORECASE -- หากต้องการจับคู่โดยไม่สนตัวพิมพ์ ให้เขียนทั้งสองกรณีลงในชุดตัวอักษร:
>>> re.search(r'[A-Fa-f0-9]{6}', 'colour #1a2b3c rest').group(0)
'1a2b3c'
คลาสตัวอักษรย่อ (\d, \s, \w และรูปแบบนิเสธ) สามารถใช้ภายใน [...] ได้เช่นกัน: [\w-] หมายถึง "ตัวอักษรคำหรือยัติภังค์ตัวอักษรจริง"
2.36.5. Greedy vs lazy quantifiers¶
ตัวกำหนดจำนวน *, +, ? และ {m,n} เป็นแบบ greedy โดยค่าเริ่มต้น -- จับคู่ตัวอักษรให้มากที่สุดเท่าที่ส่วนที่เหลือของรูปแบบยังอนุญาต บ่อยครั้งนั่นคือสิ่งที่ต้องการ แต่บางครั้งก็ไม่ใช่:
>>> re.search(r'<(.+)>', 'a <b> <c> d').group(1)
'b> <c'
แบบ greedy .+ จับคู่ไปจนถึง > ตัวสุดท้าย การต่อ ? เข้าไปทำให้ตัวกำหนดจำนวนเป็นแบบ lazy -- จับคู่ให้น้อยที่สุดเท่าที่เป็นไปได้:
>>> re.search(r'<(.+?)>', 'a <b> <c> d').group(1)
'b'
รูปแบบ lazy จะหยุดที่ > ตัวแรก Lazy quantifiers ปรากฏบ่อยมากเมื่อดึงตัวคั่นแบบสมดุลออกจากสตริง
2.36.6. การอ้างอิงย้อนกลับในการแทนที่ (Backreferences in substitution)¶
re.sub() สามารถอ้างถึงกลุ่มที่จับไว้ในสตริงการแทนที่ผ่าน \1, \2, ... การแทนที่จะเขียนซ้ำทุกการจับคู่โดยใช้ชิ้นส่วนที่จับไว้:
>>> re.sub(r'(\d+)\.(\d+)', r'\2.\1', 'swap 12.34 and 5.6')
'swap 34.12 and 6.5'
แต่ละการจับคู่จับตัวเลขสองตัวและการแทนที่จะสลับตำแหน่ง \g<1> เป็นไวยากรณ์ทางเลือกสำหรับสิ่งเดียวกัน -- มีประโยชน์เมื่อตัวอักษรถัดไปในการแทนที่เป็นตัวเลข (r'\g<1>0' เพื่อต่อศูนย์ตัวอักษรจริงเข้าท้ายกลุ่มที่ 1 แทนที่จะอ่านเป็น "กลุ่มที่ 10")
2.36.7. สิ่งที่ไม่รองรับ¶
การแจ้งเตือนเกี่ยวกับสิ่งที่ MicroPython re ไม่ รองรับ กรณีที่รูปแบบจาก CPython มาใช้ที่นี่แล้วทำให้คุณแปลกใจ:
Lookahead
(?=...)และ lookbehind(?<=...)-- ไม่ได้นำไปใช้งานกลุ่มที่มีชื่อ
(?P<name>...)และการอ้างอิงย้อนกลับที่มีชื่อ(?P=name)-- ไม่ได้นำไปใช้งานค่าคงที่แฟล็กเช่น
re.IGNORECASE,re.MULTILINE,re.DOTALL-- ไม่ถูกปฏิบัติตาม สร้างชุดที่ไม่สนตัวพิมพ์หรือแยกข้อมูลไว้ล่วงหน้าด้วยตัวเองเมธอด
match.groups(),match.span(),match.start()และmatch.end()ถูกควบคุมโดยระดับ ROM ที่บอร์ด OpenMV ที่จำหน่ายไม่มีบอร์ดใดเปิดใช้งาน โค้ดที่พึ่งพาเมธอดเหล่านี้จะไม่ทำงานบนกล้อง
ด้วยรูปแบบ กลุ่ม และจุดยึด ชุดเครื่องมือ regex บนกล้องนั้นมีขนาดเล็กพอที่จะเรียนรู้ได้ในครั้งเดียวและมีความสมบูรณ์พอที่จะทำได้ทุกอย่างยกเว้นการแยกวิเคราะห์แบบ context-sensitive