Menu

Regex ב-JavaScript: test, match, replace וקבוצות לכידה

איך ביטויים רגולריים עובדים ב-JavaScript: יצירת תבניות, המתודות המרכזיות (test, match, replace), דגלים וקבוצות לכידה עם דוגמאות אמיתיות.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

Regex הוא תבנית להתאמת טקסט

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

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

ה-i בסוף הוא דגל. i אומר שאין הבחנה בין אותיות גדולות לקטנות. עוד על דגלים בעוד רגע.

test: האם יש התאמה?

הדבר הכי מהיר שאפשר לשאול regex הוא "האם המחרוזת הזו מכילה התאמה?". זה test:

\d פירושו "כל ספרה". test מחזיר true או false ותו לא. אם כל מה שצריך הוא תשובה של כן או לא, כמו אימות שדה או סינון מערך, test הוא הכלי הנכון.

match: שליפת הטקסט שהתאים

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

בלי הדגל g, match מחזירה מערך עם ההתאמה הראשונה ועוד מטא-דאטה (index, input). עם g, היא מחזירה כל התאמה כמערך פשוט של מחרוזות. אם שום דבר לא מתאים, מקבלים null, לא מערך ריק, אז הגנו על הקוד בהתאם:

דגלים משנים את התנהגות התבנית

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

  • g: גלובלי, מוצא כל התאמה במקום רק את הראשונה.
  • i: בלי הבחנה בין אותיות גדולות לקטנות.
  • m: מרובה שורות, כך ש-^ ו-$ מתאימים לתחילת שורה וסוף שורה, לא רק לתחילת המחרוזת וסופה.
  • s: dotall, כך ש-. מתאים גם לשורות חדשות.
  • u: מודע ל-Unicode, נדרש להרבה תבניות של אימוג'י ותווים שאינם ASCII.

בלי m, ^ מעוגן רק לתחילת המחרוזת. עם m, הוא מעוגן לתחילת כל שורה, כך שגם Roses וגם Violets נתפסים.

מחלקות תווים וכמתים

אבני הבניין של רוב התבניות:

  • \d ספרה, \w תו מילה (אות, ספרה או קו תחתון), \s רווח לבן.
  • [abc] אחד מ-a, b או c. [^abc] כל דבר חוץ מהם. [a-z] טווח.
  • . כל תו חוץ משורה חדשה.
  • * אפס או יותר, + אחד או יותר, ? אפס או אחד.
  • {3} בדיוק שלושה, {2,5} בין שניים לחמישה, {2,} שניים או יותר.
  • ^ התחלה, $ סוף.

ביחד:

\b הוא גבול מילה: הקו הבלתי נראה בין תו מילה לתו שאינו תו מילה. שימושי כשרוצים התאמות של "מילה שלמה".

קבוצות לכידה: לזכור חלקים מההתאמה

סוגריים יוצרים קבוצה שלוכדת את מה שהתאים לה. exec ו-match מחזירים את הלכידות האלה לצד ההתאמה המלאה:

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

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

replace: שכתוב הטקסט שהתאים

replace מקבלת תבנית וערך חלופי. הערך החלופי יכול להיות מחרוזת או פונקציה:

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

מחרוזות חלופיות תומכות בהפניות לאחור. $1, $2 וכן הלאה מפנים לקבוצות לכידה; $<name> מפנה לקבוצות עם שם:

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

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

matchAll: כל התאמה עם הקבוצות שלה

String.prototype.matchAll מחזירה איטרטור של כל ההתאמות עם קבוצות הלכידה שלהן, דבר ש-match רגילה עם הדגל g לא יכולה לעשות:

matchAll מחייבת את הדגל g. בלעדיו תקבלו TypeError. פרשו אותה למערך ([...text.matchAll(email)]) אם צריך גישה אקראית במקום מעבר איטרטיבי.

Escaping של תווים מיוחדים

לתווים כמו . * + ? ( ) [ ] { } | \ ^ $ יש משמעות מיוחדת ב-regex. כדי להתאים אותם כפשוטם, הוסיפו לפניהם לוכסן הפוך:

הגרסה בלי ה-escape מתאימה ל-examplexcom כי . פירושו "כל תו". באג כזה נפוץ, והוא גם שקט. אם regex מתאים ליותר מדי, חפשו קודם . בלי escape.

כשבונים תבנית מקלט של משתמש, חייבים לבצע לה escape, אחרת משתמש יכול להזריק תחביר regex:

$& במחרוזת חלופית הוא קיצור של "ההתאמה המלאה".

Lookahead ו-Lookbehind

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

  • (?= ...) lookahead חיובי: "ואחריו".
  • (?<= ...) lookbehind חיובי: "ולפניו".
  • (?! ...) ו-(?<! ...) הן הגרסאות השליליות.

lookarounds לא צורכים תווים, כך שהחלק שבו אתם "מסתכלים" נשאר זמין לחלק הבא של התבנית.

מילה על אימות אימייל

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

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

מלכודות נפוצות

כמה מלכודות ששווה להפנים:

  • לשכוח את g עם replace או matchAll. רק ההתאמה הראשונה, או TypeError.
  • lastIndex עם מצב ב-regex גלובלי. regex עם הדגל g או y זוכר איפה עצר בין קריאות ל-test/exec. אל תשתמשו באותו regex שוב עבור מחרוזות לא קשורות: צרו אחד חדש, או השתמשו ב-matchAll.
  • נקודות ולוכסנים בלי escape בתבניות דינמיות. תמיד בצעו escape לקלט של משתמש לפני שמכניסים אותו ל-new RegExp(...).
  • Catastrophic backtracking. כמתים מקוננים כמו (a+)+ על קלט בעייתי יכולים להקפיא לשונית. אם regex מרגיש איטי, פשטו אותו.

הבא בתור: תאריכים ושעות

regex מטפל בצורה של טקסט; נתונים אמיתיים מגיעים גם עם חותמות זמן שצריך לנתח, לעצב ולחשב איתן. הדף הבא מכסה את Date, את Intl.DateTimeFormat ואת המודל המחשבתי שמרחיק באגים של אזורי זמן.

שאלות נפוצות

איך יוצרים regex ב-JavaScript?

יש שתי דרכים. הצורה הליטרלית משתמשת בלוכסנים: /hello/i. הבנאי מקבל מחרוזת: new RegExp('hello', 'i'). השתמשו בצורה הליטרלית כשהתבנית קבועה, ובבנאי כשצריך לבנות את התבנית ממשתנה בזמן ריצה.

מה ההבדל בין test, match ו-exec ב-regex של JavaScript?

regex.test(str) מחזיר ערך בוליאני, והוא המהיר ביותר כשרק רוצים לדעת אם יש התאמה. str.match(regex) מחזיר מערך של התאמות (או null). regex.exec(str) מחזיר התאמה אחת בכל פעם עם קבוצות הלכידה, ועם הדגל g הוא עוקב אחרי המיקום בין קריאות דרך lastIndex.

איך מחליפים את כל המופעים עם regex ב-JavaScript?

השתמשו בדגל g: str.replace(/foo/g, 'bar'). בלי g, רק ההתאמה הראשונה מוחלפת. אפשר גם לקרוא ל-str.replaceAll(/foo/g, 'bar'), אבל replaceAll עם ארגומנט regex מחייב את הדגל g, אחרת נזרקת שגיאה.

מה הן קבוצות לכידה ב-regex של JavaScript?

סוגריים בתבנית יוצרים קבוצות לכידה שזוכרות את מה שהתאים להן. /(\d{4})-(\d{2})/.exec('2024-11') מחזיר מערך שבו אינדקס 1 הוא '2024' ואינדקס 2 הוא '11'. אפשר לתת להן שמות עם (?<year>\d{4}) ולגשת אליהן דרך match.groups.year.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל