كم مرة يظهر الحرف «أ» في كلمة «أبراكادابرا»؟ نحل المسألة بلغة C#
تخيل: أنت تكتب محلل سجلات، أو تقوم بتحليل ملف CSV، أو تتحقق ببساطة من عدد المرات التي أدخل فيها المستخدم الرمز @ في البريد الإلكتروني. مهمة «حساب عدد مرات ظهور حرف أو سلسلة فرعية في نص» هي واحدة من تلك المهام التي تظهر في كل مشروع ثانٍ. على Stack Overflow، جمع هذا السؤال آلاف المشاهدات وعشرات الإجابات المتنوعة. ليس من المستغرب: في C# هناك على الأقل 5 طرق مختلفة، واختيار الطريقة الصحيحة يمكن أن يوفر ساعات من التصحيح.
في هذه المقالة، سنستعرض جميع الطرق الشائعة - من الحلقة التقليدية إلى Span<T> الحديثة و LINQ. ستعرف أي طريقة أسرع، وأيها أكثر قابلية للقراءة، وأيها من الأفضل عدم استخدامها في بيئة الإنتاج. هيا بنا!
1. الحلقة التقليدية foreach - طريقة الجد
الطريقة الأكثر بديهية: نأخذ النص، ونمر على كل حرف ونحسب التطابقات. تعمل دائمًا، ولا تتطلب مكتبات إضافية.
string text = "أبراكادابرا";char target = 'أ';int count = 0;
foreach (char c in text){ if (c == target) count++;}
Console.WriteLine($"الحرف '{target}' يظهر {count} مرة(مرات)."); // الإخراج: 4الإيجابيات: قابلية القراءة، التحكم في العملية، يمكن إضافة منطق (مثل تجاهل حالة الأحرف).
السلبيات: غير مناسب لحساب السلاسل الفرعية (عدة أحرف متتالية) - سيتطلب ذلك كتابة حلقة متداخلة.
2. طريقة Count() من LINQ - سطر واحد للأحرف
إذا كنت تحب الكود المختصر وتستخدم LINQ، فهذا الخيار مناسب لك. تأخذ طريقة Count() مُسندًا (predicate) وتعيد عدد العناصر التي تحقق الشرط.
using System.Linq;
string text = "Hello, World!";char target = 'l';
int count = text.Count(c => c == target);Console.WriteLine(count); // الإخراج: 3لاحظ: Count() هي طريقة امتداد من مساحة الأسماء System.Linq. إذا لم تقم بتضمين using System.Linq، فلن يتم تجميع الكود.
لحساب سلسلة فرعية، هذه الطريقة غير مناسبة بشكل مباشر. ستحتاج أولاً إلى تقسيم النص أو استخدام التعبيرات النمطية (انظر القسم 5).
3. Span<T> و MemoryExtensions - الأداء الأقصى
إذا كنت تعمل مع نصوص كبيرة (ميجابايت من السجلات) وكل ملي ثانية مهمة، انسَ LINQ. استخدم Span<char> وطريقة Count() من MemoryExtensions (متاحة من .NET 5+).
using System;
string text = "أبراكادابرا";ReadOnlySpan<char> span = text.AsSpan();int count = span.Count('أ');
Console.WriteLine(count); // الإخراج: 4لماذا هذا سريع؟ يعمل Span<T> مع كتل ذاكرة متصلة دون تخصيص كائنات جديدة. طريقة Count() في MemoryExtensions محسّنة لتعليمات SIMD - فهي تعالج 16-32 حرفًا في دورة معالج واحدة.
للحصول على سلاسل فرعية، يوجد حل أيضًا:
int count = span.Count("را"); // يحسب مرات ظهور السلسلة الفرعية "را"Console.WriteLine(count); // الإخراج: 2 ("أبراكادابرا" تحتوي على "را" مرتين)مهم: هذه الطريقة حساسة لحالة الأحرف. إذا كنت بحاجة إلى تجاهل حالة الأحرف، قم أولاً بتحويل النص إلى حالة واحدة أو استخدم span.Count("را", StringComparison.OrdinalIgnoreCase).
4. طريقة Split - خدعة ذكية للسلاسل الفرعية
طريقة قديمة ولكنها فعالة: قم بتقسيم النص بناءً على السلسلة الفرعية المطلوبة واحسب عدد الأجزاء ناقص واحد.
string text = "واحد، اثنان، ثلاثة، واحد، أربعة";string target = "واحد";
int count = text.Split(target, StringSplitOptions.None).Length - 1;Console.WriteLine(count); // الإخراج: 2كيف يعمل هذا: Split