• בלוג
  • השיטה הבטוחה לבנות ביטוי רגולארי לכל טקסט

השיטה הבטוחה לבנות ביטוי רגולארי לכל טקסט

07/12/2020

בחידה של לפני כמה ימים ב Advent Of Code קיבלנו משימת עיבוד טקסט שהזכירה לי כמה כיף לעבוד עם ביטויים רגולאריים, ואת השיטה הבטוחה לבנות ביטוי רגולארי מוצלח לכל מחרוזת טקסט. בחידה המקורית קיבלנו מחרוזת טקסט שנראית כך:

1-3 l:hello world

החלק שמשמאל לנקודותיים מתאר כלל והחלק שמימין לנקודותיים מתאר שורת טקסט. הכלל תמיד מורכב משני מספרים ואות. המספרים מגדירים כמה פעמים האות צריכה להופיע במחרוזת שבצד ימין (מינימום ומקסימום). במחרוזת הדוגמה אנחנו רוצים לוודא ש l מופיעה בין פעם אחת ל-3 פעמים בטקסט hello world.

קל מאוד לכתוב פונקציה בפייתון שעונה על השאלה, אם אני מקבל את המידע מופרד:

from collections import Counter

def valid(min_count, max_count, char, text):
    min_count = int(min_count)
    max_count = int(max_count)

    return min_count <= Counter(text)[char] <= max_count

אבל - איך נשבור את הטקסט לארבעת הפרמטרים? כאן נכנסת השיטה הבטוחה שתמיד עובדת לכתיבת ביטויים רגולאריים:

  1. מתחילים עם הטקסט המקורי

  2. מחליפים כל חלק שיכול להשתנות בביטוי שמייצג את התבנית הכללית

  3. נותנים שם לכל חלק

במקרה שלנו מה שיכול להשתנות זה המספרים (תבנית של סיפרה היא [0-9] ואם רוצים כמה ספרות מוסיפים + אחרי), האות (תבנית של אות היא [a-z]) והטקסט שאחרי הנקודותיים (שם יכול להיות כל דבר כלומר .+. לכן הביטוי הרגולארי יהיה:

[0-9]+-[0-9]+ [a-z]:.*

עכשיו נמשיך לתת שם לכל אחד מהחלקים:

(?P<min_count>[0-9]+)-(?P<max_count>[0-9]+) (?P<char>[a-z]):(?P<text>.*)

ואנחנו מוכנים להשתמש בקוד מתוך Python:

pattern = re.compile(r'(?P<min_count>[0-9]+)-(?P<max_count>[0-9]+) (?P<char>[a-z]):(?P<text>.*)')

if m := pattern.search("1-3 l:hello world"):
    print(valid(**m.groupdict()))
else:
    print("String does not match format")