יסודות#

הביטוי הרגולרי השימושי הקטן ביותר הוא מחרוזת פשוטה. "Hello World" =~ /World/ שואל: האם המחרוזת משמאל מכילה את התבנית מימין? היא מכילה, ולכן הביטוי הוא אמת.

if ("Hello World" =~ /World/) {
    print "matched\n";
}

ה־// עוטף את התבנית. אופרטור ה־=~ קושר את התבנית למחרוזת שיש לבדוק. ללא אופרטור קישור, Perl מחילה את התבנית על $_ במקום זאת.

אופרטור ההתאמה#

הצורה הארוכה היא m//:

"Hello World" =~ m/World/;
"Hello World" =~ m!World!;    # alternate delimiters
"Hello World" =~ m{World};    # paired delimiters

m מאפשר לבחור כל מתחם שהוא. זה חשוב כאשר התבנית עצמה מכילה את המתחם ברירת־המחדל / — יש להשוות

"/usr/bin/perl" =~ /\/usr\/bin\/perl/;  # leaning toothpick syndrome
"/usr/bin/perl" =~ m!/usr/bin/perl!;    # clearer

מתחמים זוגיים ({}, (), [], <>) קיננים, מה שמועיל כאשר התבנית מכילה את תו המתחם.

ללא m, הקו האלכסוני המוביל הוא חובה: רק /pat/. עם m, ה־m המוביל הוא חובה: m{pat}, ולא {pat}.

פינה קטנה אך מועילה: m'' (גרשיים בודדים כמתחמים) הופך את התבנית לדמוית־מירכאה־בודדת — אין שיבוץ משתנים, אין תווי escape של מירכאות כפולות. מועיל כאשר התבנית אמורה להכיל $ או @ מילוליים ואין רצון לבצע להם escape.

'price: $10' =~ m'\$\d+';      # $-as-anchor would be /$\d+/
'price: $10' =~ m'$10';        # literal '$10' — no interpolation

אופרטורי regex אחרים#

m// הוא אחד מארבעה:

אופרטור

תכלית

m//

התאמה — האם התבנית מתאימה למחרוזת?

s///

החלפה — החלפת ההתאמה במשהו אחר

qr//

הידור עצם תבנית לשימוש חוזר

tr/// (גם y///)

תרגום תו־אחר־תו

tr/// אינו אופרטור regex למרות שהוא שוכן באותה שכונה — הוא מבצע תרגום מחלקת־תווים, לא התאמת תבנית. הוא מוזכר לשם השלמות; ראו tr לסמנטיקה שלו.

m, s ו־qr חולקים את תחביר ה־regex המכוסה במדריך זה. ההבדלים הם במה שהם עושים עם התאמה מוצלחת.

שימוש חוזר בתבנית באמצעות qr//#

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

my $word = qr/\b[a-z]+\b/;

for my $line (@lines) {
    while ($line =~ /$word/g) {
        print "$&\n";
    }
}

עצם ה־qr// המהודר מוטמע בתבניות אחרות באמצעות שיבוץ. הוא גם מאפשר לבנות תבניות מחלקים בעלי שם, מה שהופך לחיוני עבור כל regex שאורכו מעל עשר שורות בערך:

my $name   = qr/[A-Z][a-z]+/;
my $number = qr/\d+/;
my $entry  = qr/$name \s+ $number/x;

"Alice 42" =~ /^$entry$/;   # matches

אותו יתרון של הידור־פעם־אחת, מבוטא ברמת ההפשטה המתאימה.

קישור: =~ ו־!~#

=~ שואל ״האם יש התאמה?״. !~ שואל ״האם יש כשל בהתאמה?״.

$s = "Hello World";

print "yes\n" if $s =~ /World/;   # yes
print "no\n"  if $s !~ /planet/;  # no

!~ אינו מבנה regex נפרד — הוא הקישור השלילי. הוא שקול ל־not ($s =~ /pat/).

התאמה מול $_#

אם הקישור מושמט, ההתאמה היא מול $_:

for ("cat", "dog", "bird") {
    print "has an 'o'\n" if /o/;   # implicit: $_ =~ /o/
}

זוהי דרך הביטוי המקובלת בלולאות while (<>), בתוך grep ו־map, ובתוך לולאות for שקובעות את $_.

רגישות לאותיות גדולות וקטנות והעיגון ברירת־המחדל#

ההתאמות רגישות לאותיות גדולות וקטנות ובלתי־מעוגנות:

"Hello" =~ /hello/;    # does not match — case differs
"Hello" =~ /ell/;      # matches — inside the string is fine

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

כאשר תבנית יכולה להתאים בכמה מיקומים, Perl מנסה משמאל ולוקחת את הראשון שעובד:

"That hat is red" =~ /hat/;   # matches 'hat' in 'That', not in 'hat'

הכלל ״ההתאמה השמאלית ביותר מנצחת״ הוא יסודי וגובר על כל העדפת התאמה אחרת: התאמה במיקום מוקדם יותר תמיד עדיפה על התאמה במיקום מאוחר יותר, ללא תלות בבחירות הפנימיות שעושה כל התאמה. לעיתים נקרא הדבר תכונת ההתקדמות הצעדית (bump-along): המנוע מנסה להתאים במיקום 0; אם הוא נכשל, הוא מתקדם למיקום 1; אם הוא נכשל, למיקום 2; וכן הלאה, ומחזיר את ההצלחה הראשונה.

מטה־תווים#

רוב התווים בתבנית מתאימים את עצמם. אלה אינם:

{ } [ ] ( ) ^ $ . | * + ? \

שניים נוספים מיוחדים רק בהקשרים מסוימים:

  • - הוא מטה־תו רק בתוך מחלקת תווים, שם הוא יוצר טווח ([a-z]). מחוץ ל־[…] הוא מילולי.

  • # הוא מטה־תו רק תחת הדגל /x, שם הוא פותח הערה עד סוף השורה. ללא /x הוא מילולי.

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

"2+2=4" =~ /2+2/;    # fails — '+' is a quantifier, needs escaping
"2+2=4" =~ /2\+2/;   # matches

"end." =~ /end\./;   # matches a literal dot
"end." =~ /end./;    # also matches — but . matches any character,
                     # so this would also match "endx", "end ", etc.

הלוכסן ההפוך עצמו הוא מטה־תו, ולכן לוכסן הפוך מילולי בתבנית דורש \\:

'C:\WIN32' =~ /C:\\WIN/;    # matches

מטה־תו שאין לו דבר מיוחד לעשות בהקשרו חוזר להתאים את עצמו. } רק סוגר מכמת {…}; מחוץ להקשר זה הוא } מילולי. זה נוח אך קל לקריאה שגויה; ראו מצב מחמיר להלן.

מצב מחמיר: use re 'strict'#

use re 'strict' הופך רישול ב־regex שנסבל בעבר לשגיאות בזמן הידור. יש להשתמש בו כאשר רוצים שמהדר ה־regex יסמן תבניות שכנראה מתכוונות למשהו שונה ממה שנכתב:

use re 'strict';

/abc{,1/;        # error: unescaped '{' in non-quantifier context
/(?-p)/;         # error: useless negation of always-on flag
/[a-]/;          # error: dash at end of class

המצב המחמיר הוא לפי תחום לקסיקלי, ולכן ניתן להפעילו עבור מודולים עתירי־regex מבלי להשפיע על קוד אחר. הוא אינו ברירת־מחדל מכיוון שהיה שובר תבניות ישנות עובדות; קוד חדש כדאי שישקול אותו.

רצפי escape#

תווים לא־מודפסים משתמשים באותם רצפי escape כמו במחרוזות במירכאות כפולות:

רצף

מתאים

\t

טאב

\n

שורה חדשה

\r

החזרת גררה

\f

הזנת טופס

\a

התראה (פעמון)

\e

escape (\x1B)

\0

בית NUL

\xHH

בית בערך הקסדצימלי HH

\x{…}

נקודת קוד Unicode בערך הקסדצימלי

\o{…}

נקודת קוד אוקטלית

\cX

control-X

\N{…}

תו Unicode לפי שם

"1000\t2000" =~ /0\t2/;      # matches
"a\x{263a}b" =~ /\x{263a}/;  # matches U+263A, WHITE SMILING FACE

הסיפור המלא של Unicode נמצא בפרק unicode.

משתנים בתבניות#

תבנית משובצת (כברירת מחדל) כמו מחרוזת במירכאות כפולות, כך שהמשתנים מוחלפים לפני ההתאמה:

my $word = "house";
"housecat" =~ /$word/;       # matches
"housecat" =~ /${word}cat/;  # matches — braces disambiguate

כדי להתאים $ או @ מילולי, יש לבצע לו escape:

'price: $10' =~ /\$10/;      # matches a literal dollar sign

אם מחרוזת שסופקה על־ידי המשתמש תשובץ לתבנית ויש רצון שהמטה־תווים שלה יטופלו כמילוליים, יש להשתמש ב־quotemeta — או במקבילה התוך־תבניתית שלו \Q…\E:

my $input = "1+1";
"1+1=2" =~ /\Q$input\E/;     # matches the literal string

ללא \Q…\E ה־+ היה נקרא כמכמת.

כיצד נקרא regex#

Perl קוראת תבנית regex בארבעה שלבים. הכרת השלבים מסבירה כמה שאלות מסוג ״מדוע זה עובד?״:

  1. שלב A: המנתח מזהה את המתחם ומוצא את סוף התבנית. הערות (?#…) מוסרות.

  2. שלב B: התבנית מנותחת כמחרוזת דמוית־מירכאות־כפולות — משתנים משובצים, רצפי escape מתבשלים, \Q…\E מתורגם ל־escape בסגנון quotemeta.

  3. שלב C: תחת /x או /xx, רווחים לבנים בלא־escape והערות שאחרי # מוסרים.

  4. שלב D: מהדר ה־regex קורא את התוצאה והופך אותה לצורה הפנימית של המנוע.

הסדר חשוב מכיוון ששלבים B ו־D פועלים על ייצוגים שונים. \Q$dir\E מתבשל בשלב B, לפני שמהדר ה־regex רואה אותו — עד לשלב D, תוכן המשתנה כבר עבר escape, ומהדר ה־regex רואה תבנית מילולית. לעומת זאת, \U…\E מפורש על־ידי שלב B כהוראת בישול־מחרוזת (להפוך את התוכן לאותיות גדולות), מה שכמעט בוודאות אינו מה שרוצים בתוך regex. המוסכמה היא: \Q ו־\E למטרות regex; \U, \L, \u, \l רק כאשר ידוע מה עושים.

(?#comment) מוסרת לפני ששלב B רואה את התבנית כלל. # מילולי בתוך (?#…) הוא תקין. ) מילולי אינו — ההערה מסתיימת ב־) הראשונה.

ההתאמה המוקדמת ביותר מנצחת — ההתקדמות הצעדית#

פרידל מנסח זאת בדיוק:

ההתאמה שמתחילה הכי מוקדם מנצחת.

מיקום 0 נבדק תחילה, אחר כך 1, אחר כך 2, וכן הלאה. המנוע לעולם אינו מעדיף התאמה מאוחרת יותר, ארוכה יותר או ״אסתטית יותר״ על פני אחת מוקדמת יותר. זה כל כך יסודי שהוא מעצב כל כלל אחר במדריך זה:

  • ״מכמתים חמדנים תופסים כמה שאפשר״ — במיקום ההתחלה הנוכחי. התפיסה מתרחשת לאחר שההתקדמות הצעדית בחרה את המיקום.

  • ״החלופה השמאלית ביותר מנצחת״ — במיקום ההתחלה הנוכחי. החלופה הנבחרת משפיעה על מה שנלכד אך לא על המקום שבו ההתאמה מתחילה.

  • עוגנים כמו ^ מגבילים את אילו מיקומים חוקיים, ולא מה מועדף מבין החוקיים.

לומר מה שמתכוונים#

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

/-?[0-9]*\.?[0-9]*/

בקריאה כאנגלית: ״סימן אופציונלי, ספרות אופציונליות, נקודה עשרונית אופציונלית, ספרות אופציונליות.״ בקריאה כקוד: כל חלק הוא אופציונלי, ולכן התבנית מתאימה למחרוזת הריקה — בתחילת כל קלט, לפני שהמנוע אפילו הסתכל. בהחלתה על ״nothing here״ היא מתאימה במיקום 0, לוכדת את המחרוזת הריקה, ומחזירה הצלחה.

התיקון הוא לדרוש לפחות ספרה אחת בלפחות צד אחד של הנקודה העשרונית:

/-?[0-9]+(\.[0-9]*)?|-?\.[0-9]+/

שתי חלופות, כל אחת דורשת לפחות ספרה אחת. התבנית מתאימה כעת למה שמחברה התכוון.

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

החלפה במבט חטוף#

החלפת טקסט משתמשת באופרטור s///, המקבל תבנית ומחרוזת החלפה:

my $x = "feed the cat";
$x =~ s/cat/dog/;            # $x is now "feed the dog"

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

לאן ממשיכים מכאן#

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

אם קוראים את המדריך לשאלה ספציפית, הפרקים עצמאיים ו־index מפרט את כולם. תבניות שנקראות בסדר אך רצות שעות מכוסות ב־performance — בספק, התשובה בדרך כלל שם.

ראו גם#

  • m — מדריך עיון מלא לאופרטור ההתאמה.

  • s — מדריך עיון מלא להחלפה.

  • qr — הידור עצם תבנית.

  • quotemeta — escape של מחרוזת לשיבוץ בטוח בתבנית.

  • פרק character classes — מה בא לאחר התאמה מילולית.

  • פרק performance — מה יכול להשתבש.