דילוג לתוכן
ביטויים רגולריים

regex למתחילים: התווים, הכמתים והקבוצות שצריך להכיר באמת

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

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

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

מה צריך להכין מראש לפני שמתחילים

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

  • Python 3 מותקן במחשב, להרצת דוגמאות עם המודול הסטנדרטי re.
  • טרמינל בלינוקס או macOS עם bash ופקודת grep.
  • דפדפן או Node.js להרצת דוגמאות JavaScript בסיסיות.
  • עורך טקסט פשוט וקובץ קטן לניסוי.

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

regex בפועל: לחשוב על תבנית, לא על מחרוזת אחת

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

הדרך הנכונה לגשת ל־regex היא בשלבים:

  1. הגדירו מה אתם רוצים למצוא: מספר? תאריך? שורה שלמה? חלק מתוך שורה?
  2. כתבו דוגמאות חיוביות: טקסטים שצריכים להתאים.
  3. כתבו דוגמאות שליליות: טקסטים שאסור שיתאימו.
  4. בנו את הביטוי לאט: תו אחד, אחר כך כמת, אחר כך קבוצה.
  5. בדקו בקוד אמיתי, לא רק בראש.

תווים רגילים ותווים מיוחדים

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

תו משמעות נפוצה דוגמה
. כל תו יחיד, מלבד ירידת שורה בדרך כלל c.t יתאים ל־cat, cot
\d ספרה \d\d יתאים ל־42
\w תו מילה: אות, ספרה או קו תחתון \w+
\s רווח, טאב או ירידת שורה \s+
^ תחילת מחרוזת או שורה ^ERROR
$ סוף מחרוזת או שורה done$

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

מחלקות תווים: לבחור מתוך רשימה

מחלקת תווים נכתבת בסוגריים מרובעים. היא אומרת: “במקום הזה מותר אחד מהתווים הבאים”.

  • [abc] — יתאים ל־a, b או c.
  • [0-9] — כל ספרה.
  • [a-z] — אות אנגלית קטנה.
  • [A-Za-z] — אות אנגלית קטנה או גדולה.
  • [^0-9] — כל תו שאינו ספרה.

שימו לב שהסימן ^ משנה משמעות בתוך סוגריים מרובעים כשהוא מופיע בתחילת המחלקה: שם הוא אומר “לא”. מחוץ לסוגריים הוא מסמן תחילת שורה או מחרוזת.

כמתים: כמה פעמים התבנית חוזרת

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

כמת משמעות דוגמה
* אפס פעמים או יותר ab*c מתאים ל־ac, abc, abbbc
+ פעם אחת או יותר ab+c לא מתאים ל־ac
? אפס או פעם אחת colou?r מתאים ל־color ול־colour
{3} בדיוק שלוש פעמים \d{3}
{2,5} בין פעמיים לחמש פעמים [a-z]{2,5}
{2,} לפחות פעמיים \d{2,}

כברירת מחדל, רוב הכמתים הם חמדניים: הם יתפסו כמה שיותר טקסט ועדיין יאפשרו לביטוי כולו להתאים. כדי להפוך אותם ל“עצלנים” מוסיפים ?, למשל .*?. זה חשוב כשמחפשים משהו בין שני סימנים, כמו טקסט בין מרכאות.

דוגמת Python מלאה: בדיקת כתובות אימייל פשוטות

הדוגמה הבאה משתמשת ב־re.fullmatch, כלומר כל המחרוזת חייבת להתאים לביטוי, לא רק חלק ממנה.

python
import re

pattern = re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}")

emails = [
    "[email protected]",
    "[email protected]",
    "missing-at.example.com",
    "bad@",
]

for email in emails:
    if pattern.fullmatch(email):
        print(f"OK: {email}")
    else:
        print(f"NO: {email}")

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

text
OK: [email protected]
OK: [email protected]
NO: missing-at.example.com
NO: bad@

שימו לב לשימוש ב־r"...". זו מחרוזת גולמית בפייתון, והיא מקלה על כתיבת לוכסנים כמו \d או \. בלי להסתבך עם בריחות כפולות.

קבוצות: לתפוס חלקים מתוך ההתאמה

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

  • (ab)+ — הרצף ab פעם אחת או יותר.
  • (cat|dog) — או cat או dog.
  • (?:abc) — קבוצה לא לוכדת; שימושית לארגון בלי לשמור תוצאה.
  • (?P<name>...) — קבוצה בשם בפייתון.

קבוצות הן המקום שבו regex מתחיל להיות שימושי באמת: לא רק לבדוק “האם יש התאמה”, אלא גם להוציא ממנה מידע מסודר.

דוגמת Python מלאה: חילוץ חלקים מכתובת URL

python
import re

text = "Visit https://example.org/docs or http://localhost:8000/test"

pattern = re.compile(
    r"(?P<scheme>https?)://(?P<host>[A-Za-z0-9.-]+)(?::(?P<port>\d+))?(?P<path>/\S*)?"
)

for match in pattern.finditer(text):
    print("scheme:", match.group("scheme"))
    print("host:", match.group("host"))
    print("port:", match.group("port") or "none")
    print("path:", match.group("path") or "/")
    print("---")

הביטוי מחפש כתובות שמתחילות ב־http או https, אחריהן שם מארח, יציאה אופציונלית ונתיב אופציונלי. הקבוצות בשם מאפשרות לגשת לחלקים דרך match.group("host") במקום לזכור מספרי קבוצות. הפלט הצפוי:

text
scheme: https
host: example.org
port: none
path: /docs
---
scheme: http
host: localhost
port: 8000
path: /test
---

שימו לב לקבוצה (?::(?P<port>\d+))?. הנקודתיים עצמן נמצאות בתוך קבוצה לא לוכדת, וכל הקבוצה אופציונלית. כך הביטוי לא דורש יציאה בכל כתובת.

עוגנים וגבולות מילים

עוגנים אינם “תופסים” תווים, אלא מסמנים מיקום:

  • ^ — תחילת שורה או מחרוזת.
  • $ — סוף שורה או מחרוזת.
  • \b — גבול מילה.

ההבדל בין cat לבין \bcat\b גדול: הראשון יתאים גם בתוך concatenate, והשני יתאים רק למילה cat כיחידה נפרדת. כשמחפשים מילים, שמות פקודות או קודים, גבול מילה מונע הרבה התאמות שגויות.

דוגמת Linux/macOS bash: סינון שורות עם grep

הפקודה הבאה מתאימה ל־Linux/macOS עם bash. היא יוצרת קובץ זמני קטן ומסננת מתוכו שורות שנראות כמו מזהה מוצר פשוט.

bash
cat <<'EOF' > /tmp/products.txt
ABC-123
abc-123
TOOL-9876
BAD-12X
EOF

grep -E '^[A-Z]+-[0-9]{3,4}$' /tmp/products.txt

הביטוי דורש תחילת שורה, אותיות גדולות באנגלית, מקף, שלוש או ארבע ספרות, ואז סוף שורה. לכן abc-123 נפסל בגלל אותיות קטנות, ו־BAD-12X נפסל בגלל האות בסוף. הפלט הצפוי:

text
ABC-123
TOOL-9876

אם אתם עובדים ב־Windows PowerShell, התחביר של יצירת הקובץ והחיפוש שונה, אבל רעיון הביטוי עצמו דומה. חשוב לא להניח שכל פקודה ב־bash תרוץ כמו שהיא ב־PowerShell.

דוגמת JavaScript מלאה: החלפת תאריכים לפורמט אחר

ב־JavaScript, ביטויים רגולריים נכתבים לעיתים בין לוכסנים. הדוגמה הבאה מחפשת תאריכים בפורמט YYYY-MM-DD ומחליפה אותם ל־DD/MM/YYYY.

javascript
const text = "Created: 2026-10-26, Updated: 2026-11-05";

const result = text.replace(/(\d{4})-(\d{2})-(\d{2})/g, "$3/$2/$1");

console.log(result);

הקבוצות מסודרות כך: שנה, חודש, יום. במחרוזת ההחלפה משתמשים ב־$1, $2, $3 כדי להפנות לקבוצות שנלכדו. הפלט הצפוי:

text
Created: 26/10/2026, Updated: 05/11/2026

הדגל g אומר “גלובלי”: החלף את כל ההתאמות, לא רק את הראשונה. בלי הדגל הזה, רק התאריך הראשון היה משתנה.

חמדנות מול עצלות: למה הביטוי תופס יותר מדי

אחת ההפתעות הראשונות ב־regex היא שביטוי כמו ".*" עלול לתפוס יותר ממה שהתכוונתם. אם הטקסט הוא "one" and "two", הכוכבית החמדנית תנסה לתפוס מהמרכאה הראשונה ועד המרכאה האחרונה.

הפתרון הפשוט הוא להפוך את הכמת לעצלן: ".*?". אבל גם זה לא תמיד מספיק, במיוחד כשיש תווי בריחה או מבנה מורכב. לפעמים עדיף להשתמש במחלקת תווים: "[^"]*", כלומר מרכאה, אחריה אפס או יותר תווים שאינם מרכאה, ואז מרכאה סוגרת.

טעויות נפוצות והודעות שגיאה אמיתיות

שכחתם לסגור סוגריים מרובעים

בפייתון, הקוד הבא:

python
import re

re.compile("[abc")

יזרוק שגיאה בסגנון:

text
re.error: unterminated character set at position 0

המשמעות: פתחתם מחלקת תווים עם [ ולא סגרתם עם ]. התיקון:

python
import re

pattern = re.compile("[abc]")
print(bool(pattern.search("b")))

הפלט הצפוי הוא True, כי האות b נמצאת במחלקה.

התחלתם בכמת בלי משהו לפניו

הביטוי הבא אינו חוקי:

python
import re

re.compile("*abc")

השגיאה תהיה:

text
re.error: nothing to repeat at position 0

כמת כמו *, + או ? חייב להתייחס למשהו שמופיע לפניו. אם רציתם לחפש כוכבית אמיתית, צריך לברוח ממנה:

python
import re

pattern = re.compile(r"\*abc")
print(bool(pattern.fullmatch("*abc")))

הפלט הצפוי הוא True.

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

הביטוי example.com לא אומר “example נקודה com”; הוא אומר example, אחר כך כל תו, ואז com. לכן הוא יכול להתאים גם ל־exampleXcom. כדי לחפש נקודה אמיתית כתבו example\.com.

איך בונים regex בלי להסתבך

במקום לכתוב ביטוי ארוך בבת אחת, כדאי לעבוד בשיטה קבועה:

  1. התחילו מחלק קטן: למשל רק הספרות עם \d+.
  2. הוסיפו עוגנים רק כשצריך התאמה מלאה: ^\d+$.
  3. בדקו קלטים שאמורים להיכשל, לא רק קלטים יפים.
  4. השתמשו בקבוצות רק כשצריך לשלוף מידע או להפעיל כמת על רצף.
  5. תנו שם לקבוצות כשיש יותר משתיים־שלוש קבוצות.
  6. אל תנסו לפתור עם regex מבנים מקוננים ומורכבים מדי; לפעמים parser פשוט עדיף.

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

שאלות שמתחילים באמת שואלים

האם regex מתאים לאימות אימיילים?

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

מה ההבדל בין match, search ו־fullmatch בפייתון?

search מחפש התאמה בכל מקום במחרוזת. match בודק התאמה מתחילת המחרוזת. fullmatch דורש שכל המחרוזת תתאים. כשבודקים קלט של משתמש, בדרך כלל fullmatch ברור ובטוח יותר.

למה הביטוי עובד בכלי אחד ולא עובד בקוד?

כנראה בגלל הבדלים בין מנועי regex, דגלים שונים, או בריחת תווים במחרוזת של שפת התכנות. בפייתון, למשל, כדאי להשתמש במחרוזות גולמיות כמו r"\d+". ב־JavaScript התחביר של קבוצות בשם ודגלים מסוימים שונה בחלק מהסביבות.

סיכום: הבסיס שמספיק לרוב המשימות

כדי להתחיל לעבוד עם ביטויים רגולריים לא צריך לדעת הכול. מספיק לשלוט בכמה אבני יסוד: תווים מיוחדים כמו \d ו־\s, מחלקות תווים כמו [A-Z], כמתים כמו + ו־{2,5}, קבוצות, עוגנים וגבולות מילים.

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