تعلم سي شارب التعامل مع النصوص في لغة سي شارب #C


حسام برهان

يُعتبر التعامل مع النصوص من المواضيع المهمّة التي يحتاجها أيّ مبرمج. توفّر سي شارب أساليب متقدّمة للتعامل مع النصوص، سنتحدّث في هذا الدرس عن بعض هذه الأساليب. حيث سنتعلّم كيفيّة البحث عن نص محدّد ضمن نص آخر، واستبدال نص بآخر، واستخلاص جزء محدّد من نص، وضمّ النصوص.

learn-csharp-strings.png

النص في سي شارب هو سلسلة من المحارف char الواقعة بين علامتي الاقتباس المزدوج " ". لقد تعاملنا في العديد من المرّات مع النصوص في الدروس السابقة، وتعلّمنا كيف أنّ النوع string هو الذي يمثّل أي نص في سي شارب.

البحث والاستبدال ضمن نص

البحث ضمن نص

للمحارف في أيّ نص ترتيب رقمي يبدأ من الصفر ويسمى الدليل index. إذا أردنا أن نبحث عن نص محدّد ضمن نص آخر فيمكن ذلك باستخدام التابع IndexOf الذي نستدعيه من النص المراد البحث ضمنه، ونمرّر إليه النص المراد البحث عنه، فيُرجع دليل أوّل محرف للنص المطابق في حال وجوده وإلّا فيرجع 1-. كمثال على ذلك انظر الشيفرة التالية:

string text = "My friend Mohammad is a developer, Mohammad likes C#.";
int pos = text.IndexOf("Mohammad");

بعد تنفيذ الشيفرة السابقة سيحمل المتغيّر pos القيمة 10 والتي تمثّل دليل الحرف M ضمن النص text. ربما تكون قد لاحظت أنّ النص Mohammad موجود في النص text مرّتين، فأيّ دليل أعاد التابع IndexOf؟

تجري عملية البحث باستخدام التابع IndexOf حسب ترتيب إدخال المحارف في النص text، لذلك فأوّل كلمة Mohammad مطابقة يصادفها تُنهي عمليّة البحث. ففي مثالنا السابق حدثت المطابقة عند كلمة Mohammad الموجودة بعد "My friend".

أمّا إذا استخدمنا كلمة غير موجودة فسنحصل على القيمة 1- كما أسلفنا. انظر الشيفرة التالية:

string text = "My friend Mohammad is a developer, Mohammad likes C#.";
int pos = text.IndexOf("Amjad");

سيحمل المتغيّر pos القيمة 1- لأنّ النص الذي نبحث عنه غير موجود في النص text.

يخضع التابع IndexOf في الواقع لزيادة التحميل، حيث أنّ هناك 9 أشكال مختلفة له. تؤمّن هذه الأشكال المزيد من مزايا البحث، حيث يمكننا مثلًا البحث عن نص ضمن جزء محدّد من النص الموجود في text، أو ابتداءً من دليل محدّد حتى آخر النص، وهكذا.

هناك تابع آخر يسمح لنا بأن نبحث بشكل معكوس، وهو التابع LastIndexOf لفهم عمله بشكل جيّد سأعيد كتابة الشيفرة السابقة ولكن باستخدام التابع LastIndexOf:

string text = "My friend Mohammad is a developer, Mohammad likes C#.";
int pos = text.LastIndexOf("Mohammad");

بعد التنفيذ سيحمل المتغيّر pos القيمة 35، وذلك لأنّ المطابقة حدثت هذه المرّة مع كلمة Mohammad الثانية (تأتي قبل كلمة likes). حيث تمثّل القيمة 35 دليل الحرف M لهذه الكلمة بالنسبة للنص text.

ملاحظة

يمكنك أن تستخدم التابع ToUpper من أي متغيّر نصي والذي يعيد حالة الأحرف الطباعية الكبيرة للنص الموجود ضمن المتغيّر النصي دون أن يؤثّر ذلك على محتوى النص الأساسي ضمن المتغيّر. ونفس الأمر ينطبق على التابع ToLower والذي يعيد الحالة الطباعية الصغيرة. يفيد كل من التابعين السابقين أحيانًا في البحث إذا أردنا عدم التمييز بين الأحرف الطباعية الكبيرة والصغيرة عند عملية المطابقة بين الكلمات. انظر لكيفيّة استخدام هذين التابعين:

string text = "Hello!";

string upper = text.ToUpper(); //upper will contains "HELLO!"
string lower = text.ToLower(); //lower will contains "hello!"

مع الانتباه إلى أنّ قيمة المتغيّر text تبقى كما هي.

الاستبدال ضمن نص

بالنسبة للاستبدال فالأمر يسير أيضًا، حيث يمكن استخدام التابع Replace لهذا الغرض. يُستدعى هذا التابع من النص الذي نريد إجراء عمليّة الاستبدال ضمنه، حيث يتطلّب هذا التابع وسيطين: الأوّل oldValue والذي يمثّل القيمة النصيّة المراد استبدالها، والوسيط الثاني newValue الذي يمثّل القيمة النصيّة الجديدة. يُرجع هذا التابع قيمة نصيّة تمثّل النص الجديد بعد إجراء عمليّة الاستبدال ضمنه، أي أنّ النص الأساسي يبقى دون تغيير. انظر الشيفرة التالية:

string text = "My friend Mohammad is a developer, Mohammad likes C#.";
string newText = text.Replace("Mohammad", "Amjad");

بعد التنفيذ سيحمل المتغيّر newText القيمة النصيّة التالية:

"My friend Amjad is a developer, Amjad likes C#."

وسيبقى النص الأصلي text على حاله دون تغيير.

استخلاص النصوص وضمها

استخلاص النصوص

يمكننا استخلاص جزء من نص باستخدام التابع SubString والذي يعني نصًّا فرعيًّا أو جزئيًّا. لهذا التابع شكلان. يسمح الشكل الأوّل باستخلاص نص جزئي ابتداءً من دليل محدّد حتى آخر النص. أمّا الشكل الثاني فيسمح أيضًا باستخلاص نص جزئي ابتداءً من دليل محدّد ولكن بطول محدّد أيضًا. للتمييز بين هاتين الحالتين لنكتب بعض الشيفرة:

string text = "My friend Mohammad is a developer, Mohammad likes C#.";

string text1 = text.Substring(24);

بعد التنفيذ سيحتوي المتغيّر text1 على النص: ".#developer, Mohammad likes C". أمّا إذا استخدمنا الشيفرة التالية:

string text = "My friend Mohammad is a developer, Mohammad likes C#.";

string text1 = text.Substring(24, 9);

فسيحتوي المتغيّر text1 على النص: "developer" فقط. والسبب في ذلك أنّنا بدأنا عمليّة الاستخلاص من الدليل 24 (دليل الحرف d) وبطول 9 محارف فقط مما يعني استخلاص الكلمة developer فحسب.

ضم النصوص

يمكن ضمّ النصوص اعتبارًا من نصوص أصغر باستخدام عامل الضم + حيث أنّ استخدامه بسيط. انظر الشيفرة البسيطة التالية:

string text = "My name is " + "Husam";

بعد تنفيذ هذه العبارة سيحتوي المتغيّر text على النص:

"My name is Husam"

هناك أمرٌ بسيط لكنّه مهم جرى وراء الكواليس! لقد أنشأ مترجم سي شارب كائنًا نصيًّا جديدًا ليستوعب النص الجديد، ثم وضع مرجعًا لهذا الكائن ضمن المتغيّر text. قد لا يبدو الأمر مقلقًا في حالة مثالنا البسيط هذا، ولكن تخيّل معي ماذا سيحدث في برنامج يتطلّب ضمّ مئات من النصوص مع بعضها (وهذا أمر وارد جدًّا)؟

ستحدث بالتأكيد مشاكل في الأداة، وستغص الذاكرة بمئات الكائنات النصيّة التي لا لزوم لها، والتي تنتظر دورها في التنظيف من قبل جامع النفايات GC الذي يعمل على تحرير ذاكرة تطبيقات دون نت من الكائنات غير المستخدمة في البرنامج.

الحل الأمثل في هذه الحالة هو تجنّب استخدام عامل الضم (+)، واستخدام الصنف StringBuilder الموجود ضمن نطاق الاسم System.Text الذي يحل هذه المشكلة بكفاءة عالية. لنستعرض البرنامج Lesson11_01 لهذا الغرض:

1	using System;
2	using System.Text;
3
4	namespace Lesson11_01
5	{
6	    class Program
7	    {
8	        static void Main(string[] args)
9	        {
10	            StringBuilder sb = new StringBuilder();
11	            string[] arrText = {
12	                                "C# is a powerful language.",
13	                                "It contains advanced features.",
14	                                "It makes programming tasks more easier.",
15	                                "C# is the main programming language in .NET world."
16	                                };
17
18	            foreach(string text in arrText)
19	            {
20	                sb.Append(text);
21	            }
22
23	            Console.WriteLine(sb.ToString());
24	        }
25	    }
26	}

أنشأنا المتغيّر sb من النوع StringBuilder وأسندنا إليه كائنًا من هذا النوع في السطر 10. بعد ذلك أنشأنا المصفوفة arrText بطريقة مختصرة في السطر 11. ثمّ استخدمنا حلقة foreach للمرور على جميع عناصر المصفوفة arrText وإضافتها واحدًا تلو الآخر إلى المتغيّر sb من خلال التابع Append الذي يتطلّب وسيطًا نصيًّا واحدًا (السطر 20). أخيرًا يعمل التابع WriteLine في السطر 23 على طباعة المحتوى النصيّ الناتج من ضمّ جميع النصوص التي أضفناها باستخدام التابع Append وذلك من خلال استدعاء التابع ToString من المتغيّر sb، ستلاحظ بعد تنفيذ البرنامج أنّ النص المعروض سيكون مضمومًا كما لو استخدمنا عامل الضم (+) على الشكل التالي:

C# is a powerful language.It contains advanced features.It makes programming tasks more easier.C# is the main programming language in .NET world.

كان من الممكن استخدام تابع آخر من المتغيّر sb في السطر 20 وهو التابع AppendLine. يشبه هذا التابع Append تمامًا باستثناء أنّه يضيف محرف سطر جديد n\ تلقائيًّا إلى كلّ نص جديد تتم إضافته.

ملاحظة

استخدمنا في السطر 11 أسلوبًا مختصرًا في إنشاء المصفوفات. يمكن استخدام هذا الأسلوب عندما تكون عناصر المصفوفة معروفة ومحدّدة. للأسلوب المختصر الشكل التالي بالنسبة للمصفوفات النصيّة:

string[] arrText = {"Text_1", "Text_2", "Text_3",  , "Text_n"};

ويمكن بالطبع استخدام هذا الأسلوب المختصر مع أيّ نوع مصفوفة آخر.

تمارين داعمة

تمرين 1

ليكن لدينا النص التالي:

"black cat and white dog are friends. black cat always brings food to dog, and white dog thanks it."

المطلوب كتابة برنامج يحسب عدد الكلمات black وعدد الكلمات dog الموجودة في النص. ثمّ يستبدل كل كلمة white في النص بكلمة brown ويعرض النص المعدّل على الشاشة.

تمرين 2

هذا التمرين هو تحدٍّ من نوع آخر!

اكتب برنامجًا يطلب من المستخدم نص كيفيّ. ثم أعدّ تقريرًا يتضمّن إحصائيّات حول عدد كل كلمة أدخلها المستخدم، واعرض هذا التقرير على الشاشة.

(تلميح: ربما تحتاج إلى شكل مختلف للتابع IndexOf يتطلّب وسيطين: الأوّل هو النص المراد البحث عنه، والثاني هو دليل المحرف الذي سيبدأ البحث اعتبارًا منه. في أمثلتنا السابقة كنّا نبحث افتراضيًّا من المحرف ذو الدليل 0).

الخلاصة

تعرّفنا في هذا الدرس على الخطوات الأساسيّة في التعامل مع النصوص. حيث تعلّمنا كيف نبحث عن نص محدّد ضمن نص آخر وذلك بأسلوبين مختلفين، بالإضافة إلى إجراء عمليّات استبدال داخل نص. كما تعلّمنا كيف نستخلص جزءًا محدّدًا من نص، وكيف نضمّ النصوص باستخدام الصنف StringBuilder. يُعتبر العمل مع النصوص أساسيًّا ومهمًّا لكلّ مبرمج وخصوصًا في مثل هذه الأيّام، فالبيانات التي يتمّ تبادلها بين التطبيقات المختلفة عبر شبكة الإنترنت تكون على شكل نصوص. سنتناول في الدرس القادم كيفيّة التعامل مع الملفات النصيّة من خلال القراءة والكتابة منها وإليها.





تفاعل الأعضاء


 لقد وصلنا بحمد الله إلى نهاية هذه الدورة, وأنا ممتن جدا لما أخذناه من وقتك واهتمامك لقراءة هذه الدروس كلمات شكر وتقدير

شارك هذا التعليق


رابط هذا التعليق
شارك على الشبكات الإجتماعية

لو سمحت
في التمرين الثاني لم أفهم ماهو المطلوب بالضبط

شارك هذا التعليق


رابط هذا التعليق
شارك على الشبكات الإجتماعية


يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن