פרק 2: קישור Perl לעמודי הרשתהתאמת מחרוזות ב Perl: ביטויים רגולרייםלמרות, שבחלק הראשון סקרנו את העקרונות הבסיסיים של שפת Perl, עדיין לא עברנו על אחד הבסיסים ביותר: התאמת מחרוזות (Pattern Matching). התאמת מחרוזות היא היבט בלתי נפרד, אם כי לעיתים מורכב, של תכנות בשפת Perl. למעשה, התאמת- מחרוזות היא הרבה פעמים הסיבה הבסיסית ביותר לשימוש בתכנות ב Perl. כמעט כל תוכנית Perl אמיתית שתיצור או תיתקל בה, תשתמש בהתאמת מחרוזות בצורה זו או אחרת. ולכן, כדאי שנתמודד עם הנושא כאן ועכשיו. כמו Perl עצמה, התאמת מחרוזות מספיק מסובכת כדי לכתוב עליה ספר שלם (למעשה יש ספר כזה), אבל אין צורך לשלוט בכל ניואנס בהתאמת מחרוזות, כדי להנות מכוחה. התאמת מחרוזות בסיסית ודאי מוכרת לכל קורא, בשורת הפקודה ב MS-DOS וב UNIX לדוגמא, באפשרותכם לבקש חיפוש בספריה בצורה: "*.txt" , שכמובן משמעותה "כל הקבצים המסתיימים ב ".txt". באותו אופן, "abc*" משמעותו כל הקבצים המתחילים ב "abc". הרעיון מאחורי התאמת מחרוזות לכן, הוא לבנות תבנית שלאחר מכן תושווה מול מידע כל שהוא- בדוגמא למעלה התבנית הייתה "*.txt" או "abc" והמידע היה כל הקבצים באותה ספריה. אם התבנית מתאימה לחלק מהמידע, תינקט פעולה כלשהי (יוצג שם הקובץ המלא), בשעה שאם אין שום התאמה לתבנית, אולי תינקט פעולה אחרת (שם הקובץ לא יוצג). בדוגמא זאת, התחביר של התאמת-המחרוזת הכיל סדרה של סמלים, שבנו את התבנית, ומשמעות הכוכבית הייתה פשוט "אפס או יותר תווים". Perl מאפשרת שיטה חזקה אך מורכת ליצירת תבניות של התאמת מחרוזות, התחביר הידוע כביטויים רגולריים (regular (expression syntax. תחביר הביטויים הרגולריים הוא ירושה מעולם ה UNIX, והוא מוגבל ל Perl. בדר"כ, תמצא אתregular expression" syntax" מקוצר לביטוי "regexps", כי הוא פשוט יותר קצר. שני השימושים הנפוצים ביותר בהתאמת מחרוזות הם: התאמה כמשפט תנאי (conditional matches) , והתאמה במשפטי החלפה (substitutions). בראשון, אנו בודקים האם יש התאמה לחלק ממידע (אמת) או אין התאמה (שקר). בשני, אנו בונים תבנית לביצוע "חפש והחלף" (search and replace) עבור תבנית מתאימה בחלק מהמידע. התאמת מחרוזות ב Perl : התאמה במשפט תנאי תוך שימוש בביטויים רגולרייםבאופן בסיסי הצורה בה משתמשים בהתאמת מחרוזות במשפטי תנאי, נראת בערך כך: $dataVariable =~ /template/ בואו נראה מה כתוב: dataVariable$ מייצג את המידע שאתם רוצים להשוות מולו. לדוגמא: נניח שאתם בודקים האם כתובת הדואר אלקטרוני של המשתמש מכילה את הסימן @ (בדיקת אימות שיגרתית למדי). כתובת הדואר אלקטרוני ,לכן, תוכנס למשתנה התאמה (match variable) בשם dataVariable$. לאחר מכן אופרטור ההתאמה ~= מסמן ל Perl שזו פעולה של התאמה. הקווים הנטויים (/) משמשים לסגירת המשפט. כעת, נתכנן תבנית שתכתב בין הקוים הנטויים. מבנה התבנית תלוי בתוצאה שאנו רוצים לקבל- מה בעצם אנחנו מנסים להשיג מההתאמה? עדיף לחשוב על מטרה זו במונחים של כללים. מטרתנו לדעת האם הסימן @ נמצא בתוך dataVariable$ , התאמה זו יכולה להיקבע ע"י הכלל "אחד או יותר תווים שאחריהם הסימן @, ואחריו שוב אחד או יותר תווים". תוכלו להבין מיד ששימוש בכלל זה, כדי לבצע אימות כתובת באמת מסובך, ויצטרך להיות הרבה יותר מפורט (לדוגמא, מקרה שבו חייב להיות סימן @ אחד בלבד), אבל נתחיל בהתאמה פשוטה זו. ראשית, נעיין בטבלה הבאה של הביטויים הרגולריים (אולי תרצו להדפיס טבלה זו). ועכשיו, נתרגם את הכלל שתיכננו, לייצוג בפקודות. $dataVariable =~ /.+\@.+/ נכון, זו הנקודה בה Perl מתחילה להיראות רע. ע"פ טבלת ה regexp, נקודה בודדת אשר מלווה בסימן + משמעותה "כל תו פעם אחת או יותר". אחרי כן מופיע, הסימן @ מלווה בקו נטוי (\) פשוט כדי לוודא ש Perl לא תפרש אותו בצורה שגויה, כחלק מפקודות regexp. כך בטוח יותר, ולעיתים אף מחויב. לדוגמא כאשר ברצונך להתאים את הסימנים כפשוטם בתוך regexp [לדוגמא כדי להתאים קו נטוי (/) תהיו חייבים להשתמש ב /\ ]. התבנית מסתיימת ברצף נוסף של "כל תו פעם אחת או יותר". בואו נתקדם צעד אחד קדימה, ונגדיר תבנית קצת יותר מדויקת. תארו לעצמכם שכתובת הדואר אלקטרוני שאנו בודקים, חייבת להסתיים בסיומת ".org" או ".net". נצטרך לשפר את הכלל לצורה: "המידע מתחיל בתו אחד או יותר, לאחר מכן הסימן @, לאחר מכן שוב תו אחד או יותר, שבסופו הרצף ".org"או ".net",שאחריו יופיע תו סוף המידע". ואו !!!, ממש אפשר להריח שהולך להיות כאן ביטוי רגולרי מסובך... אם נחזור לטבלת ה regexp, נראה שישנם סמלים, שמתייחסים לקבוצות של מידע. לדוגמא הסימן\w משמעותו כל התווים a-z,A-Z , קבוצת המספרים 0-9, וקו תחתי (_). שים לב לשימוש בסוגריים מרובעות על מנת לסגור רצף של מידע. $dataVariable =~ /^\w+\@\w+[\.org|\.net]$/i ביטויים רגולריים גדלים במהירות לגדלים מפלצתיים, ומצריכים לכן מידה מרובה של ריכוז מחשבתי. התבנית בדוגמא שלפנינו מתחילה בסימן ^ המייצג התחלה של נתונים (הוא ידוע כאחד מסימני- העוגן {anchor symbol} המשמשים להתאמה של קבוצה, יותר מאשר להתייחסות לתו כפשוטו). אחריו, מופיע הסימון לקבוצת מילים (w\), שדורש שיהיו בטקסט אחד או יותר תווים אלפבתים (או מספריים). לאחר מכן, מופיעות סוגריים שמשמשות להגדיר קבוצה לוגית, קרי, .org או .net, שימו לב לעובדה שלפני הנקודה מופיע קו נטוי (\), בגלל שאנו מחפשים נקודה בטקסט ולא כסימן. סימן ה-$ הוא סימן עוגן נוסף שמשמעותו סוף נתונים. שימו לב ל i שנמצאת אחרי הקו הנטוי הסוגר את התבנית. משמעות ה"i" היא מתאם-תבנית (pattern modifier) -במקרה זה, היא אומרת לשפת Perl, לבצע את ההתאמה בצורה בלתי-רגישה לאותיות גדולות/ קטנות (case-insensitively). לכן, כתובת המסתיימת בסיומות ".ORG"או ".NET" תהיה תקפה. אם המתאם "i" מושמט, רק ".org" יתאים, בגלל ש Perl היא בדרך כלל רגישה לאותיות גדולות/קטנות (case sensetive). התוצאה של אופרטור התאמת מחרוזות זה: הוא ערך של אמת או שקר. לכן, בצורה אופינית נמצא אותו בתוך משפטי תנאי, כמו משפט if : if ( $dataVariable =~ /^\w+\@\w+[\.org|\.net]$/i ){ ...<I>statements if match is true</I>... } התאמת מחרוזות בשפת Perl: התאמה במשפטי החלפה Substitutions)"החלפה" דומה מאד באופן בסיסי לפקודה "חפש והחלף" הנמצאת בעורכי טקסט ומעבדי תמלילים, אך בתוספת הכוח של הביטויים הרגולריים. יותר מאשר סתם להגיד שהתאמה היא אמת או שקר, אנו משתמשים בהחלפה, לשנות חלקים מהטקסט המקורי לפי תבנית כלשהי. נתחיל בדוגמא פשוטה, אנו מעונינים לנתח חלק מהמידע שהוא מידע שהמשתמש שלח על חיית המחמד שלו, ולמצוא ולהחליף כל מופע של המילה "cat" ב "feline". PRE class=jscode>$pet =~ s/\bcat\b/feline/ig בניית ההחלפה דומה מאוד לבניית תבנית ההתאמה שראינו, אבל קצת יותר מורחבת. בתחילה, שימו לב ל "s" שבאה לפני הקו נטוי הראשון. זה אומר ל Perl לבצע החלפה ולא התאמה גרידא. אחרי הקו נטוי הראשון אנו בונים את התבנית שלנו---או במילים אחרות, החלק של ה "חפש" בתבנית שלנו. אנו מחפשים את המילה "cat", אבל רק כמילה בפני עצמה, במילים אחרות אנו לא מעונינים להגדיר את "staccato" כהתאמה, וכו'. לכן, עוגן-הגבול boundary anchor), /b), עבור מילים, תוחם את המילה cat, ומחייב את Perl למצוא סימנים שאינם אותיות בשני הצדדים של "cat" (כמו רווח או סמל אחר). הקו נטוי הבא מגדיר את סוף התבנית, כמו שראינו בחיפוש כתנאי. אחרי הקו נטוי השני, אנו כותבים את התווים שמהווים את תבנית ההחלפה -במילים אחרות, החלק של "החלף ב..." בתבנית. מיד לאחר הגדרת ההחלפה (במקרה זה המחרוזת "feline", אבל אפשר כאן להשתמש גם במשתנים) ישנם שני מתאמים ((regexp modifiers, "i" שאנו כבר מכירים, כדי שלא נבדיל בין אותיות גדולות לקטנות, ו "g", שכופה החלפה כוללת (global substitution). בלי ההתאמה של "g", ההחלפה תחליף אך ורק את המופע הראשון של "cat". השימוש בהחלפה כוללת מאפשרת לנו להחליף את כל המופעים של "cat" בתוך חלק מהמידע. שימו לב, אופרטור ההחלפה עושה את השינויים ישירות למשתנה השמאלי datavariable$. במקרה אחר אולי תרצו להפנות את השינויים לעותק של datavariable$ מאשר למקור. בתחום האינטרנט, משתמשים לעיתים קרובות בהחלפות לעיבוד קלט מהמשתמש. נניח שיש לנו שורת חיפוש (search form) שמקבלת מספר מילות מפתח, שכל אחת מהן מופרדת ע"י תו שאיננו מילה, בדר"כ, פסיק. בכל אופן, כדי ליצור רשת ביטחון לתרגום שורת הקלט, אולי נרצה להחליף כל תו שאיננו מילה ואיננו רווח בפסיק, למקרה שבטעות, השתמשו בנקודה או בנקודה-פסיק או בכל סמל אחר כדי להפריד מילות מפתח. $search =~ s/[^\w| ]/,/g ניתן לתאר תבנית החלפה זו כ: "תבנית אשר לא מכילה תווים מקבוצת האותיות או רווח". התו ^ בפקודה (class specification) , אומר ל Perl להתעלם/למנוע מהתווים שנמצאים בתוך ההגדרה (הסוגריים המרובעות) מלהופיע. במילים אחרות, נחליף כל תו שאינו שייך לקבוצת האותיות (קו תחתון, (A-Z ,a-z, 0-9 או שהוא רווח, בפסיק. אם נריץ החלפה זו לדוגמה על המחרוזת: "black cat,dog*mouse/frog" מחרוזת התוצאה שנקבל תהיה: "black cat,dog,mouse,frog". שימוש נפוץ נוסף להחלפה הוא כדי להוציא תווים מסוימים מתוך מחרוזת: שוב, בדר"כ קלט של המשתמש. לדוגמא נרצה להשמיט מהקלט של המשתמש את כל סימני מעבר השורה. $userinput =~ s/\n//g בפשטות, החלפה זו מחפשת את כל סימני השורה-חדשה (n\) ומחליפה אותם בכלום, משמע הם יעלמו. כמו שאתם יכולים כבר לראות, בניית תבניות של ביטויים רגולריים, הן להתאמה והן להחלפה היא פעולה רבת עוצמה. נגענו רק בכמות קטנה מתוך הביטויים של regexp (אם כי בנפוצים ביותר). להרבה אנשים בניה, וניקוי משגיאות (debugging) של ביטויים רגולריים היא אומנות, לאחרים היא חידה, ומקור לכאבי ראש. אם יש לכם רצון, סיבולת וסקרנות ללמוד יותר על ביטויים רגולריים והתאמת מחרוזות בשפת Perl, בדקו במקומות הבאים: Regular Expression man page ו Regular Expression FAQ נסכם פרק זה במספר "החלפות" regexp שימושיות שכדאי ללגזור ולשמור:
חלק שני
|