النصوص في C#: string وأهم دوالها
اسم اللاعب في لعبتك، ورسالة الخطأ في برنامجك، وعنوان النافذة: كلها نصوص. استخدمت string والنصوص المُدرَجة بعلامة $ منذ الدروس الأولى. في هذا الدرس ستتعلم أهم دوال النصوص في C#: كيف تقطع النص وتبحث فيه وتقارنه وتنظفه، ومتى تحتاج StringBuilder.
طول النص وحروفه
Section titled “طول النص وحروفه”النص سلسلة حروف، ولكل حرف رقم يبدأ من صفر، مثل المصفوفات تماماً:
string word = "Unity";Console.WriteLine(word.Length);Console.WriteLine(word[0]);Console.WriteLine(word[^1]);Console.WriteLine(word[1..4]);5UynitLength: عدد الحروف، وهي خاصية بدون أقواس.word[0]: الحرف الأول، ونوعهchar.word[^1]: الحرف الأول من النهاية، أي الأخير.word[1..4]: من الموضع 1 إلى ما قبل الموضع 4.
تخيّل النص مثل صف مقاعد في السينما: لكل مقعد رقم، وفي كل مقعد حرف واحد.
النصوص لا تتغير
Section titled “النصوص لا تتغير”النص في C# غير قابل للتعديل (Immutable). أي دالة «تغيّر» النص تُرجع نصاً جديداً وتترك الأصلي كما هو:
string name = "sara";name.ToUpper();Console.WriteLine(name);
string upper = name.ToUpper();Console.WriteLine(upper);saraSARAالسطر الثاني بلا فائدة، لأننا لم نخزن النتيجة في متغير.
التقطيع والتنظيف
Section titled “التقطيع والتنظيف”string text = " Hello World ";string clean = text.Trim();Console.WriteLine($"[{clean}]");Console.WriteLine(clean.Substring(6));Console.WriteLine(clean.Substring(0, 5));Console.WriteLine(clean.ToLower());Console.WriteLine(clean.Replace("World", "C#"));Console.WriteLine("7".PadLeft(3, '0'));[Hello World]WorldHellohello worldHello C#007Trim(): تحذف المسافات من الطرفين. استخدمها دائماً مع ما يكتبه المستخدم.Substring(6): من الموضع 6 إلى النهاية.Substring(0, 5): من الموضع 0 وبطول 5 حروف. انتبه: الرقم الثاني هنا طول وليس موضع نهاية.Replace(a, b): تستبدل كل ظهور للنص الأول بالثاني.PadLeft(3, '0'): تكمل النص من اليسار حتى يصل طوله إلى 3.
البحث داخل النص
Section titled “البحث داخل النص”string email = "omar@example.com";Console.WriteLine(email.Contains("@"));Console.WriteLine(email.IndexOf("@"));Console.WriteLine(email.IndexOf("#"));Console.WriteLine(email.StartsWith("omar"));Console.WriteLine(email.EndsWith(".org"));
int at = email.IndexOf("@");Console.WriteLine(email[..at]);Console.WriteLine(email[(at + 1)..]);True4-1TrueFalseomarexample.comIndexOf تُرجع موضع أول ظهور، أو -1 إذا لم تجد شيئاً. ومع النطاقات [..at] و [(at + 1)..] استخرجنا اسم المستخدم والنطاق بسهولة.
المقارنة
Section titled “المقارنة”العلامة == في C# تقارن محتوى النصين مباشرة، فلا حاجة لدالة خاصة كما في Java:
string a = "unity";string b = "UNITY".ToLower();Console.WriteLine(a == b);Console.WriteLine(a == "Unity");Console.WriteLine(a.Equals("Unity", StringComparison.OrdinalIgnoreCase));Console.WriteLine(string.Compare("apple", "banana") < 0);TrueFalseTrueTrue==: هل المحتوى متطابق؟ والمقارنة حساسة لحالة الحروف.StringComparison.OrdinalIgnoreCase: لتجاهل حالة الحروف. أفضل من تحويل النصين بـToLowerثم مقارنتهما.string.Compare: مقارنة أبجدية؛ ناتج سالب يعني أن الأول يأتي قبل الثاني.
النص الفارغ و null
Section titled “النص الفارغ و null”حقل الإدخال قد يصلك فارغاً، أو مسافات فقط، أو null. دالة واحدة تفحص الحالات الثلاث:
string?[] inputs = { "علي", "", " ", null };foreach (string? input in inputs){ if (string.IsNullOrWhiteSpace(input)) { Console.WriteLine("الاسم مطلوب"); } else { Console.WriteLine($"مرحباً {input}"); }}مرحباً عليالاسم مطلوبالاسم مطلوبالاسم مطلوبالتقسيم والدمج
Section titled “التقسيم والدمج”string line = "تفاح,موز,عنب";string[] fruits = line.Split(',');Console.WriteLine(fruits.Length);Console.WriteLine(fruits[1]);
string joined = string.Join(" - ", fruits);Console.WriteLine(joined);3موزتفاح - موز - عنبSplit تقطع النص عند الفاصل وتُرجع مصفوفة، و string.Join تفعل العكس. ستحتاجهما عند قراءة ملفات البيانات أو إدخال المستخدم.
النصوص الحرفية ومتعددة الأسطر
Section titled “النصوص الحرفية ومتعددة الأسطر”الشرطة المائلة \ لها معنى خاص داخل النص (\n سطر جديد)، وهذا مزعج مع مسارات ويندوز. العلامة @ قبل النص تلغي هذا المعنى، وثلاث علامات تنصيص تكتب نصاً على عدة أسطر:
string path = @"C:\Users\Omar\notes.txt";Console.WriteLine(path);
string player = "ليلى";string message = $""" مرحباً {player}، نتيجتك: {40 + 2} """;Console.WriteLine(message);C:\Users\Omar\notes.txtمرحباً ليلى،نتيجتك: 42النص الخام """ (منذ C# 11) يحذف الإزاحة المشتركة تلقائياً، ويقبل الإدراج إذا سبقته $.
StringBuilder: بناء النصوص بكفاءة
Section titled “StringBuilder: بناء النصوص بكفاءة”لأن النص لا يتغير، فكل + داخل حلقة ينشئ نصاً جديداً كاملاً ويرمي القديم. مع آلاف التكرارات يصبح ذلك بطيئاً. الحل هو StringBuilder: نص قابل للتعديل تضيف إليه، ثم تحوّله إلى string في النهاية:
using System.Text;
var sb = new StringBuilder();for (int i = 1; i <= 5; i++){ sb.Append(i); sb.Append(' ');}sb.AppendLine("!");sb.Append("النهاية");Console.WriteLine(sb.ToString());Console.WriteLine(sb.Length);1 2 3 4 5 !النهاية20الفرق مثل كتابة كل مسودة على ورقة جديدة ورمي السابقة، مقابل الكتابة على سبورة تضيف إليها كما تشاء. لدمج نصين أو ثلاثة يكفي + أو $، أما داخل الحلقات فاستخدم StringBuilder.
جرّب بنفسك
Section titled “جرّب بنفسك”- خزّن جملة في متغير، واطبع عدد حروفها وعدد كلماتها باستخدام
Split(' '). - خزّن اسم ملف مثل
photo.png، واستخرج الامتداد باستخدامLastIndexOf('.')والنطاقات. - اكتب دالة
Initials(string fullName)تُرجع الحرف الأول من كل كلمة:"Omar Ali"تصبح"OA". - تحدٍّ: اكتب دالة
IsPalindrome(string word)تُرجعtrueإذا كانت الكلمة تُقرأ من الجهتين بنفس الشكل مثلlevel، مع تجاهل حالة الحروف.
أخطاء شائعة
Section titled “أخطاء شائعة”- تجاهل القيمة المُرجعة:
text.Trim();وحدها لا تغيّرtext. اكتبtext = text.Trim();. - الخلط في Substring:
"Hello".Substring(1, 3)تُرجعell(ثلاثة حروف)، وليس الحروف من 1 إلى 3. الرقم الثاني طول. - موضع خارج الحدود:
"Unity"[5]يرميIndexOutOfRangeException، فآخر موضع هو 4. - استدعاء دالة على null:
name.LengthوnameقيمتهnullيرميNullReferenceException. افحص أولاً، أو استخدمname?.Length.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما ناتج "Hello".Substring(1, 3)؟
الرقم الثاني في Substring هو الطول: نبدأ من الموضع 1 ونأخذ ثلاثة حروف.
سؤال
أي دالة تفحص أن النص ليس null وليس فارغاً وليس مسافات فقط؟
تُرجع true في الحالات الثلاث كلها، فهي الأنسب لفحص حقول الإدخال.
الخلاصة
Section titled “الخلاصة”- النص سلسلة حروف مرقمة من صفر:
Lengthو[i]و[^1]والنطاقات[a..b]. - النصوص لا تتغير: كل دالة تُرجع نصاً جديداً يجب أن تخزنه.
==تقارن المحتوى، وOrdinalIgnoreCaseتتجاهل حالة الحروف.Splitوstring.Joinللتقسيم والدمج، وIsNullOrWhiteSpaceلفحص الحقول.- لبناء نص داخل حلقة استخدم
StringBuilderثمToString().