التعبير النمطي (regular expression أو 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 للأنماط
تستخدم صياغة التعابير النمطية الشرطات المائلة العكسية في كل مكان (\d و\s و\b)، وكذلك هروب النصوص في C#. في النص العادي لا تُترجم "\d" أصلًا (CS1009، unrecognized escape sequence) وتُترجم "\b" إلى محرف مسح للخلف، فيتغيّر النمط بصمت. اكتب الأنماط كـ نصوص حرفية 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
في النص الحرفي تُكتب علامة الاقتباس المزدوجة "". وتتجنّب النصوص الحرفية الخام في C# 11 ("""...""") حتى ذلك.
المجموعات: استخراج أجزاء من التطابق
تنشئ الأقواس مجموعة، ويكون نص كل مجموعة متاحًا بعد التطابق. تُرقّم المجموعات من 1 بترتيب أقواس فتحها؛ والمجموعة 0 هي التطابق كله. المجموعات المسمّاة، (?<name>...)، أسهل قراءة وتنجو من تعديلات النمط:
المخرجات:
2026-03-14
ERROR
payments
Card declined for order 1042
60 by 90, area 5400
لـ [ و] معنى في التعابير النمطية (فئة محارف)، فمطابقة القوسين الحرفيين تحتاج إلى \[ و\]. وكذلك . * + ? ( ) { } ^ $ | \. ولمطابقة نص يقدّمه المستخدم حرفيًا داخل نمط مرّره عبر Regex.Escape، التي تهرّب المحارف الخاصة عنك: تعيد Regex.Escape("price (USD)") القيمة price\ \(USD\) (تُهرّب المسافات أيضًا، وهذا غير ضار).
الأقواس التي تجمّع فقط، دون التقاط، تُكتب (?:...). تبقي ترقيم المجموعات نظيفًا وهي أسرع قليلًا.
Replace: الاستبدالات وتعابير lambda
تستبدل Regex.Replace كل تطابق. في نص الاستبدال تُدرج $1 المجموعة 1، و${name} مجموعة مسمّاة، و$0 التطابق كله. ولأي شيء لا يستطيع القالب التعبير عنه مرّر دالة (عادة تعبير lambda) تتلقى كل 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 مرة واحدة، لتعبير نمطي يُستخدم مرات كثيرة.
التحقق من المدخلات: المرتكزات ومسألة البريد
للتحقق ارتكز النمط بـ ^ و$، وإلا ينجح بمجرد أن يطابق جزء من المدخلات:
المخرجات:
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 إن كان يُنفَّذ آلاف المرات، مقايضًا بدءًا أبطأ بمطابقة أسرع.
المهل الزمنية والتراجع الكارثي
بعض الأنماط تستغرق زمنًا أُسيًّا على مدخلات معينة. المثال التقليدي كمّي متداخل مثل ^(a+)+$ مقابل "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!": يجرّب المحرك كل طريقة لتقسيم حروف a بين علامتي + قبل أن يستسلم. على خادم ويب يمكن لمدخل واحد كهذا من مستخدم أن يشغل نواة معالج بالكامل (هجوم "ReDoS"). امنح أي تعبير نمطي يعالج مدخلات غير موثوقة مهلة زمنية:
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، وهو محرك يضمن زمنًا خطيًا على حساب بعض الميزات (النظرات المحيطة والإحالات الخلفية).
GeneratedRegex (.NET 7)
أضافت .NET 7 مولّد مصدر يحوّل النمط إلى شيفرة C# عادية وقت الترجمة. تحصل على سرعة Compiled دون كلفة بدء، والشيفرة المولّدة مقروءة في بيئة التطوير، وتعمل مع التقليم و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"مسح للخلف، لا حد كلمة. استخدم@"...". - محارف خاصة غير مهرّبة. تطابق
.أي محرف؛ فالنمط3.50يطابق أيضًا3x50. هرّب بـ\.، أو بـRegex.Escapeلمدخلات المستخدم. - الكمّيات الجشعة. تطابق
<.*>على<b>bold</b>النص كله. استخدم.*?الكسولة أو فئة منفية[^>]*. - لا مهلة زمنية على المدخلات غير الموثوقة. قد يعلّق كمّي متداخل طلبًا.
- Regex للصيغ المهيكلة. يحتاج HTML وJSON وCSV مع علامات الاقتباس إلى محلّل، لا إلى نمط.
الأسئلة الشائعة
كيف أستخدم regex في C#؟
أضف using System.Text.RegularExpressions; واستدعِ الدوال الساكنة على Regex: تعيد Regex.IsMatch(input, pattern) قيمة bool، وتعيد Regex.Match أول تطابق، وRegex.Matches كل التطابقات، وتستبدل Regex.Replace، وتقسم Regex.Split. اكتب الأنماط كنصوص حرفية verbatim، @"\d+"، كي تصل الشرطات المائلة العكسية إلى محرك التعابير النمطية دون تغيير.
كيف أحصل على قيمة مجموعة من تطابق 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 للتطابق كله. وللمنطق الذي لا يستطيع القالب التعبير عنه مرّر تعبير lambda: 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 التحقق من عنوان حقًا: القواعد الكاملة تسمح بأشكال لا يعالجها أي نمط عملي، والعنوان الصحيح صياغيًا قد لا يكون موجودًا. افحص الشكل الأساسي، ثم أكّد بإرسال بريد تحقق.