• בלוג
  • הסתכלות קדימה בביטויים רגולאריים

הסתכלות קדימה בביטויים רגולאריים

06/10/2022

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

This crate provides a library for parsing, compiling, and executing regular expressions. Its syntax is similar to Perl-style regular expressions, but lacks a few features like look around and backreferences.

מה הם אותם look around features ואיזה כוחות על הם מוסיפים לביטויים רגולאריים? בואו נגלה.

1. אתגר פרל השבועי 185

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

  1. נתון קלט שנראה כמו כתובת Mac בפורמט abc1.20f1.345a; הפכו אותו לפורמט ab:c1:20:f1:34:5a.

  2. נתון קלט שנראה כמו מחרוזת טקסט עם תווי הפרדה בין חלק מהאותיות (יכולים להיות בכל מקום), לדוגמה 123.abc.420. הפכו את 4 האותיות הראשונות ל x-ים.

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

2. הסתכלות קדימה בביטויים רגולאריים

בחלק הראשון אנחנו רוצים לשנות את הפורמט של כתובת מק ולהוסיף נקודותיים בין כל שתי ספרות. החלק הקל הוא להוריד את הנקודות:

echo '1ac2.34f0.b1c2' | perl -pe 's/[^a-z0-9]//g'

1ac234f0b1c2

ויש לי גם רעיון איך להוסיף נקודותיים בין כל שני תווים:

echo '1ac2.34f0.b1c2' | perl -pe 's/[^a-z0-9]//g' | perl -pe 's/(..)/$1:/g'

אבל יש בעיה עם הפיתרון הזה. כך נראה הפלט:

1a:c2:34:f0:b1:c2:

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

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

echo '1ac2.34f0.b1c2' | perl -pe 's/[^a-z0-9]//g' | perl -pe 's/(..)(?=.)/$1:/g'

1a:c2:34:f0:b1:c2

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

echo '1ac2.34f0.b1c2' | perl -pe 's/[^a-z0-9]//g' | perl -pe 's/(..)(.)/$1:$2/g'

1a:c23:4f0:b1c:2

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