Menu

C# Regex: ביטויים רגולריים עם Match, Replace, Split וקבוצות

איך משתמשים בביטויים רגולריים ב-C# עם System.Text.RegularExpressions: IsMatch, Match ו-Matches, קבוצות ממוספרות וקבוצות עם שם, Replace עם החלפות ועם למבדה, Split, RegexOptions, מחרוזות verbatim לתבניות, אימות קלט, זמני קצוב, והתכונה GeneratedRegex של .NET 7.

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

ביטוי רגולרי (regex) הוא תבנית שמתארת טקסט: "ארבע ספרות, מקף, שתי ספרות", "מילה שמתחילה באות גדולה", "כל דבר בין סוגריים מרובעים". ב-C#, המחלקה Regex ב-System.Text.RegularExpressions מוצאת, מחלצת, מחליפה ומפצלת טקסט בעזרת תבניות כאלה.

IsMatch, Match ו-Matches

שלוש מתודות מכסות את רוב החיפושים. IsMatch עונה כן או לא, Match מחזירה את ההתאמה הראשונה, ו-Matches מחזירה את כולן:

פלט:

True
2026-03-14 at index 22
4 matches:
  1042
  2026
  1043
  2026
Success: False, Value: []

Match שנכשלה לא מחזירה null: היא מחזירה Match שה-Success שלו הוא false וה-Value שלו ריק. תמיד בדקו את Success לפני שמשתמשים בהתאמה.

החיפוש של ארבע ספרות מראה איך תבניות תופסות יותר ממה שהתכוונו. \b\d{4}\b פירושו "בדיוק ארבע ספרות כמילה שלמה", והשנים שבתוך התאריכים עונות על זה, כי - הוא גבול מילה. הדקו את התבנית כך שתגיד את מה שאתם מתכוונים, למשל (?<=Order |order )\d+, או השתמשו בקבוצות, בהמשך.

מחרוזות verbatim לתבניות

התחביר של regex משתמש בלוכסנים הפוכים בכל מקום (\d, \s, \b), וגם תווי הבריחה של מחרוזות C#. במחרוזת רגילה, "\d" אפילו לא מתקמפל (CS1009, רצף בריחה לא מוכר), ו-"\b" מתקמפל לתו backspace ומשנה את התבנית בשקט. כתבו תבניות כמחרוזות verbatim עם @, שבהן לוכסן הפוך הוא פשוט לוכסן הפוך:

var a = new Regex("\\d+\\.\\d{2}");   // escaped twice: hard to read
var b = new Regex(@"\d+\.\d{2}");     // verbatim: what the regex engine sees

במחרוזת verbatim, מירכאה כפולה נכתבת "". מחרוזות raw של C# 11 ("""...""") חוסכות גם את זה.

קבוצות: חילוץ חלקים מהתאמה

סוגריים יוצרים קבוצה, והטקסט של כל קבוצה זמין אחרי ההתאמה. הקבוצות ממוספרות מ-1 לפי סדר הסוגריים הפותחים; קבוצה 0 היא כל ההתאמה. קבוצות עם שם, (?<name>...), קריאות יותר ושורדות עריכות של התבנית:

פלט:

2026-03-14
ERROR
payments
Card declined for order 1042
60 by 90, area 5400

ל-[ ול-] יש משמעות ב-regex (מחלקת תווים), ולכן התאמה לסוגריים המילוליים דורשת \[ ו-\]. אותו דבר נכון ל-. * + ? ( ) { } ^ $ | \. כדי להתאים מחרוזת שהמשתמש סיפק באופן מילולי בתוך תבנית, העבירו אותה דרך Regex.Escape, שמבצעת בריחה לתווים המיוחדים בשבילכם: Regex.Escape("price (USD)") מחזירה price\ \(USD\) (גם רווחים עוברים בריחה, וזה לא מזיק).

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

Replace: החלפות ולמבדות

Regex.Replace מחליפה כל התאמה. במחרוזת ההחלפה, $1 מכניס את קבוצה 1, ${name} קבוצה עם שם ו-$0 את כל ההתאמה. לכל מה שתבנית לא יכולה לבטא, העבירו פונקציה (בדרך כלל למבדה) שמקבלת כל Match ומחזירה את ההחלפה שלו:

פלט:

Invoiced 14/03/2026, paid 02/04/2026.
too many spaces
Card **** **** **** 1234
Tea 2.75, Scone 3.52, Jam 1.10

מסכת הכרטיס משתמשת ב-lookahead, (?=...): היא מתאימה לספרה רק אם אחריה באות עוד ארבע ספרות, בלי לצרוך אותן. בדיקות הקשר ((?=...), (?!...), (?<=...), (?<!...)) בודקות את הסביבה בלי לכלול אותה בהתאמה, וזה מה שמאפשר ל-Replace אחד להשאיר את ארבע הספרות האחרונות שלמות.

לטקסט פשוט בלי תבנית, string.Replace פשוטה ומהירה יותר; פנו ל-Regex.Replace כשמה שצריך להחליף משתנה.

Split

Regex.Split מפצלת בכל התאמה של תבנית, וכך מטפלת במפרידים שמשתנים:

פלט:

csharp / dotnet / regex / tutorial / beginner
Order Shipped Event Handler

אם התבנית מכילה קבוצות לוכדות, Regex.Split כוללת את המפרידים שנלכדו בתוצאה; השתמשו ב-(?:...) כשאתם לא רוצים אותם.

RegexOptions

אפשרויות משנות את ההתנהגות של כל התבנית. שלבו אותן עם |:

פלט:

1
2
error INFO Error
True

אלה שתשתמשו בהן:

  • IgnoreCase: התאמה שאינה תלויה ברישיות (הצורה המוטמעת (?i)).
  • Multiline: ^ ו-$ מתאימים בתחילת ובסוף כל שורה, לא רק של כל המחרוזת.
  • Singleline: . מתאים גם ל-\n (כברירת מחדל הוא מתאים לכל תו חוץ משורה חדשה).
  • IgnorePatternWhitespace: רווחים בתבנית נשמטים ו-# מתחיל הערה, לתבניות ארוכות שנכתבות על פני כמה שורות.
  • CultureInvariant: יחד עם IgnoreCase, משווה בלי הכללים של התרבות הנוכחית.
  • Compiled: מקמפל את התבנית ל-IL פעם אחת, ל-regex שמשתמשים בו פעמים רבות.

אימות קלט: עוגנים ושאלת האימייל

באימות, עגנו את התבנית עם ^ ו-$, אחרת היא עוברת ברגע שחלק מהקלט מתאים:

פלט:

PT-1000    loose=True  strict=True
pt-1000    loose=False strict=False
XPT-1000Y  loose=True  strict=False
PT-10      loose=False strict=False
ana@example.com   True
ana@example       False
ana @example.com  False
ana@@example.com  False

תבנית האימייל בודקת רק את הצורה: משהו בלי רווחים או @, @ אחד, ודומיין עם נקודה. זה מכוון. הדקדוק האמיתי של כתובות (RFC 5322) מתיר חלקים מקומיים במירכאות, הערות ודומיינים שהם כתובות IP; תבניות שמנסות לכסות אותו מגיעות למאות תווים ועדיין דוחות כתובות תקינות שאנשים באמת משתמשים בהן. וכתובת מושלמת תחבירית עדיין יכולה לחזור. בדקו את הצורה, ואז שלחו אימייל אישור. System.Net.Mail.MailAddress מציעה בדיקת צורה נוספת אם אתם מעדיפים לא לכתוב תבנית.

ביצועים: מתודות סטטיות, מופעים, קומפילציה

המתודות הסטטיות (Regex.IsMatch(input, pattern)) מפענחות את התבנית ושומרות אותה במטמון קטן (15 תבניות כברירת מחדל), כך שקריאות חוזרות עם אותה תבנית זולות. לתבנית שמשמשת בלולאה חמה, צרו מופע Regex אחד ושמרו אותו בשדה static readonly; הוסיפו RegexOptions.Compiled אם הוא רץ אלפי פעמים, בתמורה להתחלה איטית יותר והתאמה מהירה יותר.

זמני קצוב ו-backtracking קטסטרופלי

חלק מהתבניות לוקחות זמן מעריכי על קלטים מסוימים. הדוגמה הקלאסית היא כמת מקונן כמו ^(a+)+$ מול "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!": המנוע מנסה כל דרך לחלק את ה-a בין שני ה-+ לפני שהוא מוותר. בשרת web, קלט אחד כזה ממשתמש יכול לתקוע ליבת מעבד (מתקפת "ReDoS"). תנו זמן קצוב לכל regex שמטפל בקלט לא אמין:

var pattern = new Regex(@"^(\w+\s?)*$", RegexOptions.None, TimeSpan.FromMilliseconds(100));
try
{
    bool ok = pattern.IsMatch(userInput);
}
catch (RegexMatchTimeoutException)
{
    // treat as invalid input
}

לכל מתודה סטטית יש גם העמסה שמקבלת זמן קצוב. .NET 7 הוסיפה גם את RegexOptions.NonBacktracking, מנוע שמבטיח זמן ליניארי במחיר של כמה תכונות (lookarounds, הפניות לאחור).

GeneratedRegex (.NET 7)

.NET 7 הוסיפה source generator שהופך תבנית לקוד C# רגיל בזמן קומפילציה. מקבלים את המהירות של Compiled בלי עלות אתחול, הקוד שנוצר קריא בסביבת הפיתוח, והוא עובד עם trimming ועם Native AOT:

public static partial class Patterns
{
    [GeneratedRegex(@"^[A-Z]{2}-\d{4}$", RegexOptions.IgnoreCase)]
    public static partial Regex ProductCode();
}

bool ok = Patterns.ProductCode().IsMatch("pt-1000");   // True

ב-.NET 7 ואילך זו הצורה המומלצת לכל תבנית שידועה בזמן קומפילציה.

טעויות נפוצות

  • עוגנים חסרים באימות. בלי ^...$, IsMatch מקבלת קלט שרק מכיל התאמה.
  • מחרוזות רגילות לתבניות. "\b" הוא backspace, לא גבול מילה. השתמשו ב-@"...".
  • תווים מיוחדים בלי בריחה. . מתאים לכל תו; 3.50 כתבנית מתאים גם ל-3x50. בצעו בריחה עם \., או עם Regex.Escape לקלט של משתמשים.
  • כמתים חמדנים. <.*> על <b>bold</b> מתאים לכל המחרוזת. השתמשו בגרסה העצלה .*? או במחלקה שלילית [^>]*.
  • אין זמן קצוב לקלט לא אמין. כמת מקונן יכול לתקוע בקשה.
  • regex לפורמטים מובנים. HTML, JSON ו-CSV עם מירכאות צריכים parser, לא תבנית.

שאלות נפוצות

איך משתמשים ב-regex ב-C#?

הוסיפו using System.Text.RegularExpressions; וקראו למתודות הסטטיות של Regex: Regex.IsMatch(input, pattern) מחזירה bool, Regex.Match מחזירה את ההתאמה הראשונה, Regex.Matches את כולן, Regex.Replace מחליפה ו-Regex.Split מפצלת. כתבו תבניות כמחרוזות verbatim, @"\d+", כדי שהלוכסנים ההפוכים יגיעו למנוע ה-regex בלי שינוי.

איך מקבלים ערך של קבוצה מהתאמת regex ב-C#?

שימו סוגריים סביב החלק שאתם רוצים וקראו את match.Groups[1].Value (הקבוצות ממוספרות מ-1; קבוצה 0 היא כל ההתאמה). קבוצות עם שם ברורות יותר: את (?<year>\d{4}) קוראים עם match.Groups["year"].Value. בדקו קודם את match.Success, כי בהתאמה שנכשלה הקבוצות ריקות ולא null.

איך מחליפים טקסט עם regex ב-C#?

Regex.Replace(input, pattern, replacement) מחליפה כל התאמה. מחרוזת ההחלפה יכולה להפנות לקבוצות: $1 לקבוצה ממוספרת, ${name} לקבוצה עם שם, $0 לכל ההתאמה. ללוגיקה שתבנית לא יכולה לבטא, העבירו למבדה: Regex.Replace(text, @"\d+", m => (int.Parse(m.Value) * 2).ToString()).

איך הופכים regex ב-C# ללא תלוי רישיות?

העבירו RegexOptions.IgnoreCase: Regex.IsMatch(input, "error", RegexOptions.IgnoreCase). אפשרויות משתלבות עם |, למשל RegexOptions.IgnoreCase | RegexOptions.Multiline. אפשר גם להפעיל את זה בתוך התבנית עם (?i).

איך מאמתים כתובת אימייל עם regex ב-C#?

תבנית כמו ^[^@\s]+@[^@\s]+\.[^@\s]+$ תופסת שגיאות הקלדה ברורות (חסר @, רווחים, אין נקודה בדומיין), ובדרך כלל זה כל מה שטופס צריך. regex לא באמת יכול לאמת כתובת: הדקדוק המלא מתיר צורות שאף תבנית מעשית לא מטפלת בהן, וכתובת תקינה תחבירית עלולה לא להתקיים. בדקו את הצורה הבסיסית, ואז אשרו על ידי שליחת אימייל אימות.

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

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

להתחיל