2.36. קבוצות ועוגנים#
תבנית יכולה לעשות יותר מאשר לומר ”מחרוזת זו מתאימה“ – היא יכולה לפרק את החלקים שהותאמו ולמסור כל אחד מהם ליישום לפי שם. סוגריים סביב חלק מתבנית הופכים אותו ל-קבוצה לוכדת; אובייקט ההתאמה חושף אז כל קבוצה כתת-מחרוזת נפרדת.
2.36.1. קבוצות לוכדות#
עטפו כל חלק מתבנית ב-(...) כדי ללכוד את מה שהותאם:
>>> 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.
תבנית נפוצה היא ”התאם מבנה ידוע, לכוד את החלקים המשתנים כמספרים שלמים“:
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. קבוצות שאינן לוכדות#
סוגריים גם מקבצים תת-ביטוי כך שכמַתֵּת (quantifier) יכול לחול על הקבוצה כולה. זו המטרה היחידה של הקיבוץ ב-r'(ab)+' – ”אחד או יותר של ab”. העובדה ש-ab מופיע כקבוצה 1 היא תופעת לוואי.
כדי לקבץ בלי ללכוד, השתמשו ב-(?:...)
>>> re.search(r'(?:ab)+', 'xababy').group(0)
'abab'
קבוצות שאינן לוכדות שומרות על מספרי הקבוצות מסודרים כאשר תבנית משתמשת בקיבוץ למבנה אך אינה מעוניינת לחלץ כל חלק.
2.36.3. עוגנים#
עוגנים אינם מתאימים לתו – הם מתאימים למיקום.
^– תחילת המחרוזת.$– סוף המחרוזת.
עוגנים הם מה שגורם ל-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
^ ו-$ ב-re של MicroPython תמיד מציינים את תחילת וסוף המחרוזת השלמה שהועברה ל-re.search(). אין דגל re.MULTILINE שיגרום להם להתאים בכל ירידת שורה משובצת, ו-$ אינו מתאים גם למיקום שלפני \n נגרר – הוא חייב להיות הסוף המוחלט של הקלט. כדי לקבל התנהגות לכל שורה, פצלו תחילה את הקלט לפי ירידות שורה והריצו את התבנית על כל שורה.
2.36.4. קבוצות תווים#
סוגריים מרובעים מגדירים קבוצה מפורשת של תווים. ההתאמה צורכת בדיוק תו אחד מהקבוצה.
[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 בפועל מכיוון שהטקסט המילולי הוא באותיות קטנות. ל-re של MicroPython אין דגל re.IGNORECASE – כדי להתאים ללא תלות ברישיות, כתבו את שני המקרים לתוך הקבוצה:
>>> re.search(r'[A-Fa-f0-9]{6}', 'colour #1a2b3c rest').group(0)
'1a2b3c'
קיצורי המחלקות (\d, \s, \w, וצורותיהם השליליות) ניתנים לשימוש גם בתוך [...]: [\w-] הוא ”תווי מילה או מקף מילולי.“
2.36.5. כמַתְּתִים חמדניים מול עצלים#
הכמַתְּתִים *, +, ? ו-{m,n} הם חמדניים כברירת מחדל – הם מתאימים לכמה שיותר תווים שיתר התבנית עדיין יאפשר. לעיתים קרובות זה בדיוק מה שרוצים; לעיתים לא:
>>> re.search(r'<(.+)>', 'a <b> <c> d').group(1)
'b> <c'
ה-.+ החמדני תפס עד ה-> האחרון. הוספת ? הופכת את הכמַתֵּת לעצל – הוא מתאים לכמה שפחות:
>>> re.search(r'<(.+?)>', 'a <b> <c> d').group(1)
'b'
הצורה העצלה עוצרת ב-> הראשון. כמַתְּתִים עצלים עולים תדיר בעת חילוץ תוחמים מאוזנים ממחרוזת.
2.36.6. הפניות אחורה בהחלפה#
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. מה שאינו זמין#
תזכורת למה ש-re של MicroPython אינו תומך בו, למקרה שתבנית מ-CPython מגיעה לכאן ומפתיעה אתכם:
Lookahead
(?=...)ו-lookbehind(?<=...)– לא ממומשים.קבוצות בעלות שם
(?P<name>...)והפניות אחורה בעלות שם(?P=name)– לא ממומשים.קבועי דגלים כמו
re.IGNORECASE,re.MULTILINE,re.DOTALL– אינם נתמכים. בנו בעצמכם את הקבוצה ללא תלות ברישיות או פצלו מראש את הקלט.המתודות
match.groups(),match.span(),match.start()ו-match.end()מותנות ברמת ROM שאף לוח OpenMV שנשלח אינו מפעיל. קוד שמסתמך עליהן לא יפעל על המצלמה.
עם תבניות, קבוצות ועוגנים, ערכת כלי ה-regex על המצלמה קטנה מספיק כדי ללמוד אותה בישיבה אחת ועשירה מספיק כדי לעשות הכול פרט לניתוח תלוי-הקשר.