ביטויים רגולאריים


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

1. מהם ביטויים רגולאריים

ביטוי רגולארי מתפקד כתבנית לתיאור קלט. בהקבלה לעברית קחו את הביטוי ״מספר טלפון״, ביטוי זה מתאר סוג מסוים של קלטים למשל הטקסט 054-2212121 או הטקסט 03-9112131. בהנתן שורת קלט נוכל לשאול כל דובר עברית האם השורה מכילה מספר טלפון, וברוב המקרים יהיה קל לבן אדם לענות על שאלה זו.
או בדוגמא נוספת, התיאור ״משפט הכולל מילה שמתחילה ב-ג׳״ מתאר אוסף של קלטים ביניהם המשפט ״גם אני אוהב ביטויים רגולאריים״ או ״גמלים ירוקים לא יודעים לרקוד״. כל דובר עברית שיראה את התיאור ואת הקלטים יוכל בקלות לדעת האם מדובר במשפט שמתאים לתיאור או שלא.

ביטויים רגולאריים מגדירים אוסף של קלטים כך שלמחשב יהיה קל לדעת האם קלט מתאים לתיאור או לא. לאחר שנכתוב ביטוי רגולארי עבור אוסף קלטים מסוים, נוכל לשאול בקוד התוכנית האם קלט מסוים מתאים לביטוי שכתבנו. 

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

Regexp xkcd

2. הביטוי הראשון שלי

כבר סיפרתי שביטוי רגולארי הינו בסך הכל תבנית לתיאור קלטים. התבנית הפשוטה ביותר מורכבת מתווים רגילים (למשל אותיות ומספרים). תבנית כזו תתאים לכל הקלטים המכילים אותה, או במלים אחרות ביטוי רגולארי יתאים לקלט אם ניתן למצוא מופע של התבנית בתוך הקלט.

כך למשל הביטוי הרגולארי king יתאים לכל הקלטים (שימו לב שהחלק בקלט שמתאים לתבנית מסומן):

God save the king
All the king’s horses
Four kings and a prince
The kingdom is saved

אך לא יתאים לקלט:

The queen is dead

מאחר ולא משנה כמה תתאמצו, לא תצליחו למצוא מופע של התבנית king בתוך הקלט האחרון.

3. קבוצות תווים - Character Classes

ביטויים רגולאריים יכולים לכלול גם תווים מיוחדים, וזה מה שהופך אותם לגמישים יותר מסתם חיפוש מחרוזת בתוך טקסט ארוך יותר. הסוג הראשון של תווים מיוחדים נקרא Character Classes. תווים מסוג זה מייצגים קבוצת תווי קלט אפשריים. כך למשל קבוצת התווים \d מייצגת ספרה כלשהי, וקבוצת התווים \w מייצגת אות, ספרה או קו תחתי. 

הביטוי הרגולרי

\d+\d

יתאים לכל הקלטים:

7+2
7+2=9
I have 2+2 cents

אך לא יתאים לקלט:

2 + 2 = 4

הרווחים בין הספרות לסימן הפלוס לא מאפשרים לנו למצוא מופע של התבנית בתוך שורת הקלט.

ניתן לייצר קבוצות תווים בעצמכם באמצעות שימוש בסוגריים מרובעים ורישום כל התווים בקבוצה בתוך הסוגריים המרובעים, ויש גם קבוצת תווים מפורסמת שנקראת . (כן, פשוט סימן הנקודה), הכוללת את כל התווים בעולם. שתי קבוצות מעניינות נוספות הן הקבוצה \D המהווה את המשלים לקבוצת \d, כלומר כל התווים שאינם ספרות, או הקבוצה \W המהווה את המשלים לקבוצת \w.
כך למשל הביטוי הרגולארי:

\W[A-Z]

יתאים לקלטים:

Let’s Go
Don’t Let Me Down

אך לא יתאים לקלט:

Don’t take your guns to town

מאחר והאות הגדולה היחידה בקלט לא מגיעה אחרי תו שאיננו אות, ספרה או קו תחתי.

4. כמתים - Qunatifiers

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

\d+[ ]*\d+[ ]*=[ ]*\d+

יתאים לכל הקלטים:

7 + 10 = 17
7+10=1
2+2 = 4
I don't think 2 + 2 = 5

אך לא יתאים לקלט:

10x + 12y = 50

5. מעצורים - Assertions

קבוצת הסימנים המיוחדים האחרונה נקראת Assertions. תווים מקבוצה זו אומרים משהו על היכן מבוצעת ההתאמה. כך למשל התו ^ מתאים לתחילת שורה, התו $ מתאים לסוף השורה והתו \b מתאים לגבול מילה, כלומר המקום בו מילה מתחילה או מסתיימת. 
כך למשל אם נרצה לאתר רק שורות המתחילות באות גדולה נוכל להשתמש בביטוי:

^[A-Z]

או אם נרצה למצוא משפטים המכילים את המילה נמלה אך לא כחלק ממילה אחרת, נוכל להשתמש בביטוי:

\bant\b

שיתאים לכל הקלטים:

The ant is an animal
ant ate my homework
I can see the ant

אך לא יתאים לקלט:

The elephant ate my homework

 

6. בואו נראה קצת קוד

בשפת JavaScript אנו יכולים לבדוק האם מחרוזת קלט מסוימת מתאימה לביטוי רגולארי באמצעות הפונקציה match של מחרוזת. הפונקציה מקבלת ביטוי רגולארי ומחזירה אובייקט התאמה במקרה והמחרוזת מתאימה לביטוי או את הערך null אם אין התאמה.

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

var valid = ! text.match(/\b[a-z]/);

שילוב קוד זה עם תיבת טקסט דורש קצת קוד JavaScript מסביב לטיפול באירועים ונראה כך:

השימוש בביטוי רגולארי אפשר לבדוק את הטקסט בשורת קוד אחת ובצורה מאוד פשוטה. זה הכח של ביטויים אלו הזמינים עבורכם כמעט בכל שפת תכנות.

7. סיכום וקריאת המשך

ביטויים רגולאריים כוללים עוד הרבה מאוד סימנים שלא הספקתי להראות בפוסט זה מפאת קוצר מקום. רשימה טובה של כל הסימנים זמינה בקישור:
http://www.cheatography.com/davechild/cheat-sheets/regular-expressions/

באתר הבא תוכלו להכניס ביטוי רגולארי וקלטים ולראות האם הקלט מתאים לביטוי. האתר גם כולל רשימת קיצורים ודוגמאות לביטויים רגולאריים עבור תבניות נפוצות:
http://www.regexr.com/

ולמתעניינים בתיאוריה מאחורי הקוד מוזמנים להסבר מפורט בעברית מהבלוג לא מדויק:
http://www.gadial.net/2015/01/29/regular_expressions/