מדריך PHP

מדריך PHP: ביטויים רגולריים

‏ • Internet Israel

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

הביטויים הרגולריים מאפשרים לנו לעשות מניפולציות על כל מחרוזת טקסט שהיא – בין אם לבדוק אותה (האם למשל מחרוזת הטקסט היא כתובת דואר, או האם מחרוזת הטקסט היא מספר) ובין אם לשנות אותה (לעשות copy&paste) לחלק מהטקסט. יש שני סוגים של ביטויים רגולריים ש-PHP תומכת בהם. הסוג הראשון הוא POSIX והוא מבוסס על התקנים של UNIX. הסוג השני הוא PCRE שמבוסס על ביטויים רגולריים של שפת פרל. רוב המתכנתים משתמשים ב-PCRE ואנו נתרכז בה.

הנה החלקים הרלוונטיים וההסברים בדוקומנטציה על text function.

כאשר אנו באים להשתמש בביטויים רגולריים, אנחנו צריכים ראשית כל להחליט בשביל מה בדיוק אנו זקוקים לביטויים האלו. האם אנו רוצים לחתוך חלק מהטקסט? האם אנו רוצים לבצע search&replace? האם אנו רוצים לבצע בדיקה בטקסט? ישנן מגוון של פונקציות PCRE, אנו כרגע נתמקד באחת שמטרתה למצוא טקסט. שמה בישראל הוא preg_match ותפקידה למצוא תבנית מסוים במחרוזת טקסט. אם נסתכל עליה בדוקומנטציה, אנו נראה שהיא מקבל שני ארגומנטים – הראשון הוא ה-pattern – הלא הוא הביטוי הרגולרי והשני הוא הטקסט שבו היא מבצעת את הבדיקה. במידה והבדיקה יוצאת מוצלחת (הביטוי הרגולרי נמצא) היא תחזיר true (1). במידה והבדיקה יוצאת לא מוצלחת (הביטוי הרגולרי לא נמצא) היא תחזיר false (0).

נשמע מסובך? למה שלא נדגים? יש לי את המחרוזת המופלאה ‘abc’ בואו ונבדוק אם יש בה את האות a.

<?php

$my_text = 'abc';

$result = preg_match('/a/', $my_text);

print "Result: $result";

אם נריץ את זה נראה שהתוצאה היא 1, כלומר מצאנו את a. הסלאשים הם חיוניים ב-PECL והם נקראים דילמיטרים, אפשר להשתמש גם ב-# או בסוגריים מסולסלות:

<?php

$my_text = 'abc';

$result = preg_match('{a}', $my_text);

print "Result: $result";

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

בואו נתקדם לאט ובזהירות, נניח שאני רוצה לחפש בטקסט שלי את המילה: ‘bc’ ולא את המילה a, איך אני עושה את זה? פשוט ביותר! ממש כך:

<?php

$my_text = 'abc';

$result = preg_match('/bc/', $my_text);

print "Result: $result";

טוב, זה ממש פשוט. יותר מדי פשוט, בואו נסבך את העניינים קצת. נניח שאני רוצה למצוא את צירוף המילים ab, אבל רק כאשר הוא בתחילת משפט! כלומר, abc זה בסדר אבל zabc זה לא (כי ab לא בתחילת המשפט). איך עושים את זה? בדיוק בשביל זה יש לנו ביטויים רגלוטוריים. הביטוי הראשון הוא ^ – שאם הוא נמצא בתחילת הביטוי אז מדובר בתחילת שורה:

<?php

$my_text = 'abc';

$result = preg_match('/^ab/', $my_text);

print "Result: $result";

זה יחזיר לי תוצאה, כיוון שהביטוי מתיחס לכל טקסט שמתחיל ב-ab. לפיכך זה יתפוס על טקסט abc, abcd וגם ababulele לצורך העניין. הביטוי כולל כל מחרוזת טקסט שמתחילה ב-ab.

כמו שיש לנו ביטוי שמציין התחלה, יש לנו ביטוי שמציין סוף שורה. הביטוי הזה למשל:

bc$ יציין כל טקסט שהוא שנגמר ב-bc. למשל abc או bababababc

<?php

$my_text = 'ababababababc';

$result = preg_match('/bc$/', $my_text);

print "Result: $result";

אם אנו רוצים לשלב בין שני הביטויים? למשל ביטוי שמתחיל ב-a או נגמר ב-bc? מאד פשוט! משתמשים ב-|

<?php 
$my_text = 'adfdfdfdbc';

$result = preg_match('/^a|bc$/', $my_text);

print "Result: $result";

הביטוי הרגולרי ^a|bc$ (או מתחיל ב-a או נגמר ב-bc) יתפוס לגבי abc, abcfsfbc או כל ביטוי אחר.

ואם אנו רוצים ביטוי שגם מתחיל ב-a וגם נגמר ב-bc? כלומר – ביטוי שיתפוס גם את abc, גם את agfgfgfgbc ובעצם כל דבר שמתחיל ב-a ונגמר ב-bc? כלומר, בין a לבין bc יהיו אפס או יותר תוים (כמה בדיוק? לא אכפת לנו). כך עושים את זה:

<?php

$my_text = 'adad  bc';

$result = preg_match('/^a.*bc$/', $my_text);

print "Result: $result";

לא להכנס לפאניקה! מה יש לנו פה? יש לנו סימן של תחילת שורה ^ ואז a שמראה לנו שחייב שיהיה a בהתחלה. בסוף יש לנו סימן של סוף ביטוי שמראה לנו ש-bc צריכים להיות בסוף. מה שחדש ומוזר הוא .* שני סימנים חדשים. נקודה זה כל תו שהוא – מספר, סימן מיוחד, אות בעברית, אנגלית ובעצם הכל. כוכבית היא סימן מיוחד שאומרת אפס או יותר. כלומר אני מגדיר בביטוי הזה שחייב להיות a בתחילתו, חייב להיות bc בסופו ובאמצע יכול להיות כל תו בכמות של אפס או יותר.

ואם הייתי רוצה 1 או יותר? אז זה פשוט, במקום כוכבית אפשר להשתמש בסימן +

<?php

$my_text = 'addbc';

$result = preg_match('/^a.+bc$/', $my_text);

print "Result: $result";

בביטוי כזה addbc יתפוס, גם ביטוי כמו assnfjskfjdskbc יתפוס. מה לא יתפוס? נכון abc כי בין a ל-bc צריך להיות אחד או יותר תווים.

ואם הייתי רוצה שבין a ל-b יהיה או אפס או תו אחד בלבד? גם זה פשוט, במקום + נשתמש בסימן שאלה

<?php

$my_text = 'adbc';

$result = preg_match('/^a.?bc$/', $my_text);

print "Result: $result";

בדוגמא הזו abc יתפוס, adbc יתפוס וכל ביטוי אחר שיש תו אחד בלבד בין a ל-bc. ביטוי כמו addbc שיש בו שני תוים בין a ל-bc לא יתפוס.

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

<?php

$my_text = 'aghgddbc';

$result = preg_match('/^a.{2,5}bc$/', $my_text);

print "Result: $result";

בסוגריים המסולסלות יש פירוט של שני תוים מינימום וחמישה תוים מקסימום בין ה-a ל-bc

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

<?php

$my_text = 'a@@bc';

$result = preg_match('/^a@{2,5}bc$/', $my_text);

print "Result: $result";

שימו לב לביטוי, במקום . שמתי @ ועכשיו כל מחרוזת טקסט צריכה להיות עם @ במקום עם כל תו שהוא.

אני לא חייב להיות מוגבל לתו אחד בלבד, בואו ונראה איך אפשר להשתמש גם ב-@ וגם ב-a לצורך העניין:

<?php 
$my_text = 'aaa@@bc';

$result = preg_match('/^a[@a]{2,5}bc$/', $my_text);

print "Result: $result";

איך עשיתי את זה? באמצעות סוגריים מרובעים, שבהם אני יכול לשים את כל התוים שאותם אני רוצה.

נניח שבין ה-a ל-bc אני רוצה שיהיו רק אותיות באנגלית מ-a ועד z? טוב, זה קל באמצעות סוגריים מרובעות, שם אני אוכל לשים את כל התוים שא